题目一:Count the string
思路
给定一个字符串,输出这个字符串的前缀出现次数,根据next的性质知,当next有值的时候,就证明肯定有一个匹配,所以我们统计出next数组里面>0的个数就是有多少个匹配,再加上与自身的匹配n就是答案
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 200000 + 5;
int mod = 10007;
int len,nex[maxn];
char a[maxn];
void getnext()
{
int i = 0, j = -1;
nex[0] = -1;
while(i < len) {
if(j == -1 || a[i] == a[j]){
nex[++i] = ++j;
}
else j = nex[j];
}
}
int main()
{
int t;
cin >> t;
while(t--) {
scanf("%d%s", &len, a);
getnext();
//匹配个数+字符串长度
int ans = len;
for(int i = 1; i <= len; i++) {
int tmp = nex[i];
while(tmp) {
ans = (ans+1) % mod;
tmp = nex[tmp];
}
}
printf("%d\n", ans);
}
return 0;
}
题目二:Power Strings
中文题意
题目输入若干个字符串,对于每个字符串求最短的循环节,并输出循环节的长度,若没有循环节则输出1。输入一个“ .” 表示输入结束。
思路
因为nex的定义是最长的前缀等于后缀,所以如果存在循环节时,len-nex[len]就一定是最短的那个循环节的长度,当len%(len-nex[len])==0时,说明存在循环节,否则就不存在,
证明如下:
假如 nex[len] 位置 x 时 当 len%(len-nex[len])==0 时,那么 len%(len-nex[len]-a.len-b.len)==0(因为a.len=b.len=len-nex[len]),所以中间重叠的部分也可以被分为长度为 len-nex[len] 的字符串,此时a1==b2 b2==b3 a3==b4
又因为 a==b可以推出 a1==b1==a2==a3==a4==b2==b3==b4。
所以len-nex[len]为循环节的长度。
假若 len%(len-nex[len])!=0 ,由以上可以看出中间重叠的部分不能正好分成长度为 len-nex[len] ,因此也不可能出现循环节。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1E6 + 5;
char str[maxn];
int nex[maxn], len;
void getnext()
{
int i = 0, j = -1;
memset(nex, 0, sizeof(nex));
nex[0] = -1;
while(i < len) {
if(j == -1 || str[i] == str[j])
nex[++i] = ++j;
else
j = nex[j];
}
}
int main()
{
while(scanf("%s", str) != EOF) {
if(str[0] =='.') break;
len = strlen(str);
getnext();
//例如 aaaaa len = 4, len - nex[len] = 1
if(len % (len - nex[len]) == 0) cout << len / (len - nex[len]) << endl;
else cout << "1" << endl;
}
return 0;
}
题目三:Seek the Name, Seek the Fame
思路
next[i]表示 前i位中,前缀 、后缀中最大公共子串的长度
可以知道next[n],也就是最长的公共子串长度,然后就可以枚举长度了。
然后用两个字符串数组tmp1,tmp2,分别表示前缀和后缀,然后一比较就好了,不过最初的想法比较朴素,又分别用两个函数每次重新实现,不过对于前缀来说每次加一位就好了,但是后缀就不怎么好弄了。经高人指导,用了strncmp就可以很简单的实现构造后缀
代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 400000 + 5;
char str[maxn];
int nex[maxn], ans[maxn], len;
void getnext()
{
int i=0,j=-1;
nex[0]=-1;
while(i<len)
{
if(j==-1||str[i]==str[j])
{
nex[++i] = ++j;
}
else
j=nex[j];
}
}
int main()
{
while(scanf("%s", str) != EOF) {
int cnt = 0;
len = strlen(str);
getnext();
int last = nex[len];
while(last != -1) {
ans[cnt++] = last;
last = nex[last];
}
for(int i = cnt - 2; i >= 0; i--) {
printf("%d ", ans[i]);
}
cout << len << endl;
}
return 0;
}