poj - 2751
题意:找出一个字符串中既是前缀又是后缀的字符串的长度,增序输出。
思路:我们学习了KMP,KMP中next的数组的意思为:借用了一下kuangbin大佬的:
假设主串:S: S[1] S[2] S[3] ……S[n] 模式串:T: T[1] T[2] T[3]…..T[m] 现在我们假设主串第i 个字符与模式串的第j(j<=m)个字符‘失配’后,主串第i 个字符与模式串的第k(k<j)个字符继续比较,此时就有S[i] != T[j] 主串: S[1]...S[i-j+1]...S[i-1]S[i]... ||(匹配) || ≠ 模式串: T[1]... T[j-1] T[j] 由此,可以得到关系式如下 T[1]T[2]T[3]...T[j-1] = S[i-j+1]...S[i-1] 由于S[i] != T[j],接下来S[i]将与T[k]继续比较,则模式串中的前k-1咯字符串必须满足下列关系式,并且不可能存在k'>k满足下列关系式: T[1]T[2]T[3]...T[k-1] = S[j-k+1]S[j-k+2]...S[i-1] (k<j) 也就是说: 主串: S[1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]... || || || ?(待比较) 模式串: T[1] T[2]... T[k-1] T[k] 现在可以把前面的关系综合总结如下 S[1]...S[i-j+1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]... || || || || ≠ T[1]... T[j-k+1] T[j-k+2]... T[j-1] T[j] || || || ? T[1] T[2] ... T[k-1] T[k] 现在唯一的任务就是如何求k了,通过一个next函数求。
void get_next(char *T) { int len = strlen(T); Next[0] = -1;int i = 0,j = -1; while(i < len) { if(j == -1 || T[i] == T[j]) { Next[++ i] = ++ j; } else j = Next[j]; } }
这样我们可以写一下这道题:
首先,字符串本身是字符串的前缀和后缀,我们假设不是本身的就是真子后缀。然后我们要看一下最长真子后缀是多少,当然是在len处匹配失败的值,即Next[len],因为在len处匹配失败,令k = Next[len],说明 S[0],s[1]……s[k - 1]和S[k ]……s[len - 1]对应相等,k为最长真子后缀。
同样的,此时长度为k的前缀和后缀相等,那么我们可以把一个刚刚表示的后缀当成前缀,相当于长度为k的前缀之中,因为下标是从0开始,所以在k处匹配失败,k' = Next[k],k'为第二小的真子后缀;
依次类推。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn = 4e5 + 10; typedef long long ll; #define clr(x,y) memset(x,y,sizeof x) #define INF 0x3f3f3f3f const ll Mod = 1e9 + 7; typedef pair<int,int> P; int Next[maxn]; char s[maxn],s2[maxn]; void get_next(char *T) { int len = strlen(T); Next[0] = -1;int i = 0,j = -1; while(i < len) { if(j == -1 || T[i] == T[j]) { Next[++ i] = ++ j; } else j = Next[j]; } } vector<int>ans,ans2; int main() { while( ~ scanf("%s",s)) { get_next(s); int len = strlen(s); ans2.clear(); ans2.push_back(len); int i = len; while(Next[i] > 0) { ans2.push_back(Next[i]); i = Next[i]; } for(int i = ans2.size() - 1;i >= 0; i --) printf("%d%c",ans2[i],i ? ' ' : '\n'); } return 0; }
poj2406
题意:找出字符串的最小循环节,输出循环次数。
思路:结论:如果存在循环节,那么len % (len - Next[len]) == 0,最小循环节长度为L = len - Next[len],循环次数为len/L;否则循环节就是本身。仔细思考,举几个例子,才明白。不会证明。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn = 1e6 + 10; typedef long long ll; #define clr(x,y) memset(x,y,sizeof x) #define INF 0x3f3f3f3f const ll Mod = 1e9 + 7; typedef pair<int,int> P; int Next[maxn]; char s[maxn],s2[maxn]; void get_next(char *T) { int len = strlen(T); Next[0] = -1;int i = 0,j = -1; while(i < len) { if(j == -1 || T[i] == T[j]) { Next[++ i] = ++ j; } else j = Next[j]; } } int main() { while( ~ scanf("%s",s) && s[0] != '.') { get_next(s); int len = strlen(s); if(len % (len - Next[len]) == 0) printf("%d\n",len / (len - Next[len])); else puts("1"); } return 0; }
poj 3461 kMP模板题
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn = 1e6 + 10; typedef long long ll; #define clr(x,y) memset(x,y,sizeof x) #define INF 0x3f3f3f3f const ll Mod = 1e9 + 7; typedef pair<int,int> P; char s[maxn],t[maxn]; int Next[maxn]; void get_next(char* T) { int i = 0,j = -1;Next[0]= -1; int len = strlen(T); while(i < len) { if(j == -1 || T[i] == T[j]) { Next[++ i] = ++j; } else j = Next[j]; } } int solve(char *S,char * T) { get_next(t); int len1 = strlen(S),len2 = strlen(T); int i = 0,j = 0; int ans = 0; while(i < len1) { if(j == -1 || S[i] == T[j]) { i ++;j ++; } else j = Next[j]; if(j == len2) ans ++,j = Next[j]; } return ans; } int main() { int Tcase;scanf("%d",&Tcase); while(Tcase --) { scanf("%s%s",t,s); printf("%d\n",solve(s,t)); } return 0; }