1. 程式人生 > >KMP演算法與其應用

KMP演算法與其應用

KMP字串匹配

題目連結:https://www.luogu.org/problemnew/show/P3375

1.nxt陣列:

nxt[x]:以x位結尾的字串為字尾能匹配到的最長字首。

求法見程式碼:

nxt[1]=0;int j=0;
	for(int i=2;i<=n2;i++)
	{
		while(j&&s2[j+1]!=s2[i]) j=nxt[j];//這裡是一個找最長字首的過程 
		if(s2[j+1]==s2[i]) j++;
		nxt[i]=j;
	}

然後要匹配s1和s2。其實和求nxt的方法是一樣的呢。注意f[i]:i結尾的幾位和s2前幾位相同(最長字首)

這裡注意f[i]==n2時就是匹配成功啦

j=0;
	for(int i=1;i<=n1;i++)//f[i]:i結尾的幾位和s2前幾位相同 
	{
		while(j&&s2[j+1]!=s1[i]) j=nxt[j];
		if(s2[j+1]==s1[i]) j++;
		f[i]=j;
	}
	for(int i=1;i<=n1;i++) if(f[i]==n2) cout<<i-n2+1<<endl;
	for(int i=1;i<=n2;i++) cout<<nxt[i]<<" ";
	cout<<endl;

KMP求最長週期:

題目連結:https://www.luogu.org/problemnew/show/P3435

這裡首先要搞清楚週期的定義!

最短週期:串長-最長相同前後綴長

最長週期:串長-最短相同前後綴長

因為這題是求最長週期,所以要知道最短相同前後綴長。但我們的nxt陣列求得是最長相同前後綴長,那怎麼辦呢?

不急,我們試著考慮對於每一個串它的最短後綴長一定包含在最長字尾長之中:所以不斷的找nxt,直到找到最短的非零的那個就好啦。這裡運用一個技巧:從頭往後列舉,nxt[i]直接就等於nxt[nxt[i]](如果nxt[nxt[i]]不等於0的話)

程式碼如下:

#include <iostream>
#include <cstdio>

using namespace std;
const int maxn=1000005;
char s[maxn];int nxt[maxn];
int main()
{
	int n;scanf("%d",&n);scanf("%s",(s+1));
	nxt[1]=0;int j=0;
	for(int i=2;i<=n;i++)
	{
		while(j&&s[j+1]!=s[i]) j=nxt[j];
		if(s[j+1]==s[i]) j++;
		nxt[i]=j;
	}
	for(int i=1;i<=n;i++)
	{
		if(nxt[nxt[i]]) nxt[i]=nxt[nxt[i]];	
	} 
	int ans=0;
	for(int i=1;i<=n;i++) if(nxt[i]) ans+=(i-nxt[i]);
	cout<<ans<<endl;
	return 0;
}