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;
}