KMP,ACM集训
目录
831. KMP字符串
给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P 在字符串 S 中多次作为子串出现。
求出模式串 P 在字符串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。
数据范围
1≤N≤105
1≤M≤106
输入样例:
3
aba
5
ababa
输出样例:
0 2
解析:KMP模板
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 1e5 + 5, M = 1e6 + 5;
int n, m;
string s, p;
int ne[N];
int main() {
cin >> n >> p >> m >> s;
p.insert(0," ");
s.insert(0," ");
for (int i = 2, j = 0; i <= n; i++) {
while (j && p[i] != p[j + 1])j = ne[j];
if (p[i] == p[j + 1])j++;
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i++) {
while (j && s[i] != p[j + 1])j = ne[j];
if (s[i] == p[j + 1])j++;
if (j == n) {
printf("%d ", i - n);
j = ne[j];
}
}
return 0;
}
D - Cyclic Nacklace
https://vjudge.net/contest/582298#problem/D
CC always becomes very depressed at the end of this month, he has checked his credit card yesterday, without any surprise, there are only 99.9 yuan left. he is too distressed and thinking about how to tide over the last days. Being inspired by the entrepreneurial spirit of "HDU CakeMan", he wants to sell some little things to make money. Of course, this is not an easy task.
As Christmas is around the corner, Boys are busy in choosing christmas presents to send to their girlfriends. It is believed that chain bracelet is a good choice. However, Things are not always so simple, as is known to everyone, girl's fond of the colorful decoration to make bracelet appears vivid and lively, meanwhile they want to display their mature side as college students. after CC understands the girls demands, he intends to sell the chain bracelet called CharmBracelet. The CharmBracelet is made up with colorful pearls to show girls' lively, and the most important thing is that it must be connected by a cyclic chain which means the color of pearls are cyclic connected from the left to right. And the cyclic count must be more than one. If you connect the leftmost pearl and the rightmost pearl of such chain, you can make a CharmBracelet. Just like the pictrue below, this CharmBracelet's cycle is 9 and its cyclic count is 2:
Now CC has brought in some ordinary bracelet chains, he wants to buy minimum number of pearls to make CharmBracelets so that he can save more money. but when remaking the bracelet, he can only add color pearls to the left end and right end of the chain, that is to say, adding to the middle is forbidden.
CC is satisfied with his ideas and ask you for help.
Input
The first line of the input is a single integer T ( 0 < T <= 100 ) which means the number of test cases.
Each test case contains only one line describe the original ordinary chain to be remade. Each character in the string stands for one pearl and there are 26 kinds of pearls being described by 'a' ~'z' characters. The length of the string Len: ( 3 <= Len <= 100000 ).
Output
For each case, you are required to output the minimum count of pearls added to make a CharmBracelet.
Sample
Inputcopy | Outputcopy |
---|---|
3 aaa abca abcde | 0 2 5 |
解析:KMP-next数组应用+循环字符串判断
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 1e5 + 100, M = 1e4 + 5;
char str[N];
int ne[N];
int main() {
int T;
scanf("%d", &T);
while (T--) {
//memset(str, 0, sizeof(str));//千万不要加这句,加了就会WA,我也不知道为什么
scanf("%s", str + 1);
int len = strlen(str + 1);
int n = len;
for (int i = 2, j = 0; i <= n; i++) {
while (j && str[i] != str[j + 1])j = ne[j];
if (str[i] == str[j + 1])j++;
ne[i] = j;
}
int r = len-ne[len];
if (ne[len] == 0) {
printf("%d\n", len);
}
else {
if (len % r == 0)
printf("0\n");
else
printf("%d\n", r - len % r);
}
}
return 0;
}
F - Power Strings
Nefu字符串 - Virtual Judge (vjudge.net)
Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative integer is defined in the normal way: a^0 = "" (the empty string) and a^(n+1) = a*(a^n).
Input
Each test case is a line of input representing s, a string of printable characters. The length of s will be at least 1 and will not exceed 1 million characters. A line containing a period follows the last test case.
Output
For each s you should print the largest n such that s = a^n for some string a.
Sample
Inputcopy | Outputcopy |
---|---|
abcd aaaa ababab . | 1 4 3 |
Hint
This problem has huge input, use scanf instead of cin to avoid time limit exceed.
Sponsor
解析:KMP-next数组应用+循环字符串判断
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 1e7 + 5;
int ne[N];
char str[N];
int main() {
while (scanf("%s", str + 1) != EOF) {
if (str[1] == '.')
break;
int n = strlen(str + 1);
for (int i = 2, j = 0; i <= n; i++) {
while (j && str[i] != str[j + 1])j = ne[j];
if (str[i] == str[j + 1])j++;
ne[i] = j;
}
int r = n - ne[n];
if (n % r == 0)
printf("%d\n", n / r);
else {
printf("1\n");
}
}
return 0;
}
H - Count the string
Nefu字符串 - Virtual Judge (vjudge.net)
It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example:
s: "abab"
The prefixes are: "a", "ab", "aba", "abab"
For each prefix, we can count the times it matches in s. So we can see that prefix "a" matches twice, "ab" matches twice too, "aba" matches once, and "abab" matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For "abab", it is 2 + 2 + 1 + 1 = 6.
The answer may be very large, so output the answer mod 10007.
Input
The first line is a single integer T, indicating the number of test cases.
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters.
Output
For each case, output only one number: the sum of the match times for all the prefixes of s mod 10007.
Sample
Inputcopy | Outputcopy |
---|---|
1 4 abab | 6 |
解析:next数组理解
KMP中next数组的含义:0 - i 中的最大前缀后缀匹配
预处理出next数组,可知每个next数组记录该串的最大前缀的长度(数值上等价于下标)。那么对于某一个长位n的子串s,除了 它本身和模板串匹配外,还有它的最大相同后缀。
那么, ans = 本身的一个 + 最大相同后缀
关于不重不漏,由于答案的第一部分的头部都是模板串的第一个字符,对不同长度一定不同,对第二部分,末尾字符不会相同,同样不会重复
虽然next数组统计的时最大前后缀,但大的后缀有前缀,那么大后缀中的小后缀一定也有前缀
注意:这道题中相同的部分不应该重叠,如:aaa 的答案为5
所以当前后缀相交时,此部分不计入答案。
好好理解理解!!!!
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 2e5 + 5, P = 131,mod= 10007;
int n;
char str[N];
int ne[N];
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%s",&n, str + 1);
for (int i = 2, j = 0; i <= n; i++) {
while (j && str[i] != str[j + 1])j = ne[j];
if (str[i] == str[j+1])j++;
ne[i] = j;
}
LL ans = 0;
for (int i = 1; i <= n; i++) {
if (ne[i] != 0)
ans=(ans+1)%mod;
}
ans = (ans + n) % mod;
printf("%lld\n", ans);
}
return 0;
}
J - String Problem
Nefu字符串 - Virtual Judge (vjudge.net)
Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Input
Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters.
Output
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Sample
Inputcopy | Outputcopy |
---|---|
abcder aaaaaa ababab | 1 1 6 1 1 6 1 6 1 3 2 3 |
解析:kmp求循环节,最小/最大表示法
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 1e6 + 5;
string str;
int ne[N];
int minrepresentation(string s) {
int len = s.length();
s = " " + s + s;
int i = 1, j = 2;
while (j <= len) {
int k = 0;
while (k < len && s[i + k] == s[j + k])k++;
if (s[i + k] > s[j + k])i += k + 1;
else j += k + 1;
if (i == j)j++;
if (i > j)swap(i, j);
}
return i;
}
int maxrepresentation(string s) {
int len = s.length();
s = " " + s + s;
int i = 1, j = 2;
while (j <= len) {
int k = 0;
while (k < len && s[i + k] == s[j + k])k++;
if (s[i + k] < s[j + k])i += k + 1;
else j += k + 1;
if (i == j)j++;
if (i > j)swap(i, j);
}
return i;
}
int main() {
while (cin >> str) {
int mn = minrepresentation(str);
int mx = maxrepresentation(str);
str.insert(0, " ");
int n = str.length();
n--;
for (int i = 2, j = 0; i <= n; i++) {
while (j && str[i] != str[j + 1])j = ne[j];
if (str[i] == str[j + 1])j++;
ne[i] = j;
}
int r = n - ne[n];
int ans = n % r ? 1 : n / r;
printf("%d %d %d %d\n", mn, ans, mx, ans);
}
return 0;
}