KMP Next陣列的應用入門
阿新 • • 發佈:2019-01-29
poj - 2751
題意:找出一個字串中既是字首又是字尾的字串的長度,增序輸出。
思路:我們學習了KMP,KMP中next的陣列的意思為:借用了一下kuangbin大佬的:
求Next的方法為:假設主串: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;
}