1. 程式人生 > >洛谷 P3375 【模板】KMP字符串匹配 || HDU 1686 Oulipo || kmp

洛谷 P3375 【模板】KMP字符串匹配 || HDU 1686 Oulipo || kmp

含義 發生 cstring .net 這就是 hdu image dba jin

HDU-1686

P3375

kmp介紹:

http://www.cnblogs.com/SYCstudio/p/7194315.html

http://blog.chinaunix.net/uid-8735300-id-2017161.html(mp&kmp)

http://www-igm.univ-mlv.fr/~lecroq/string/node8.html(mp&kmp,看上去很正確的例程)

http://blog.csdn.net/joylnwang/article/details/6778316

洛谷P3375:

字符串下標從1開始:

 1 #include<cstdio>
 2
#include<cstring> 3 char s1[1000010],s[1010]; 4 int f[1010]; 5 int n,m; 6 void getf() 7 { 8 int i,j=0; 9 f[0]=-1;f[1]=0; 10 for(i=2;i<=m;i++) 11 { 12 //j=f[i-1]其實開始的時候j是這個值 13 while(j>=0&&s[j+1]!=s[i]) j=f[j]; 14 j++; 15 /*這一步後就是滿足s[1..j]==s[i-j+1..i]
16 顯然如果找不到任何j那麽j應該為0,而-1+1=0,所以f[0]=-1, 17 否則應當為s[1..i-1]的某個相同的前綴後綴且它們之後都為s[i]*/ 18 f[i]=j; 19 } 20 } 21 void kmp() 22 { 23 int ans=0,i,j=0; 24 for(i=1;i<=n;i++) 25 { 26 //初始時j為s[1..j]==s[i-1-j+1..i-1] 27 while(j>=0&&s[j+1]!=s1[i]) j=f[j];
28 j++; 29 //此時使得s往後移到了正確的位置 30 /*例如ABABABC/ABC:i=3時j=1,也就是s開頭1位跟s1[1..i]對齊 31 又如ABDBA/ABC:i=3時j=0,也就是s開頭0位跟s1[1..i]對齊*/ 32 if(j==m) 33 { 34 //ans++; 35 printf("%d\n",i-m+1); 36 j=f[j]; 37 } 38 } 39 //return ans; 40 } 41 int main() 42 { 43 int i; 44 scanf("%s%s",s1+1,s+1); 45 n=strlen(s1+1); 46 m=strlen(s+1); 47 getf(); 48 kmp(); 49 for(i=1;i<=m;i++) 50 printf("%d ",f[i]); 51 return 0; 52 }

字符串下標從0開始,加上某個優化後開兩個f:

 1 #include<cstdio>
 2 #include<cstring>
 3 int f[1010],f2[1010];
 4 char s1[1000100],s[1010];
 5 int n,m;
 6 void getf()
 7 {
 8     int i=0,j=f[0]=-1;
 9     while(i<m)
10     {
11         while(j>=0&&s[j]!=s[i])    j=f[j];
12         ++i;++j;
13         f[i]=s[i]==s[j]?f[j]:j;
14         f2[i]=j;
15     }
16 }
17 void kmp()
18 {
19     int i=0,j=0;
20     while(i<n)
21     {
22         while(j>=0&&s[j]!=s1[i])    j=f[j];
23         ++i;++j;
24         if(j==m)    printf("%d\n",i-m+1);
25     }
26 }
27 int main()
28 {
29     int i;
30     scanf("%s%s",s1,s);
31     n=strlen(s1);
32     m=strlen(s);
33     getf();
34     kmp();
35     for(i=1;i<=m;i++)
36         printf("%d ",f2[i]);
37     return 0;
38 }

HDU-1686:

程序1的f[i]的含義是能使s[0..p-1]==s[i-p..i-1]的最大的p。字符串下標從0開始。也就是如果當對齊原串下標i的是模板串下標j時發生失配,可以將對齊原串下標i的改為模板串下標f[j]。

程序1(藍書模板)(f[0]定義為0):

 1 #include<cstdio>
 2 #include<cstring>
 3 int f[10100];
 4 char s1[1000100],s[10100];
 5 void getFail(char *P,int *f)
 6 {
 7     int m=strlen(P);
 8     f[0]=0;f[1]=0;
 9     for(int i=1;i<m;i++)
10     {
11         int j=f[i];
12         while(j&&P[i]!=P[j])    j=f[j];
13         f[i+1]=P[i]==P[j]?j+1:0;
14     }
15 }
16 int find(char *T,char *P,int *f)
17 {
18     int n=strlen(T),m=strlen(P),ans=0;
19     getFail(P,f);
20     int j=0;
21     for(int i=0;i<n;i++)
22     {
23         while(j&&P[j]!=T[i])    j=f[j];
24         if(P[j]==T[i])    j++;
25         if(j==m)    ans++;
26     }
27     return ans;
28 }
29 int main()
30 {
31     int t;
32     scanf("%d",&t);
33     while(t--)
34     {
35         scanf("%s%s",s,s1);
36         printf("%d\n",find(s1,s,f));
37     }
38     return 0;
39 }

程序2(魔改藍書&某個例程)(f[0]定義為-1,已經加了某個優化):

 1 #include<cstdio>
 2 #include<cstring>
 3 int f2[10100];
 4 char s1[1000100],s[10010];
 5 void getFail(char *P,int *f)
 6 {
 7     int m=strlen(P),j=f[0]=-1,i=0;//i初始值不能為1
 8     while(i<m)
 9     {
10         while(j>=0&&P[i]!=P[j])    j=f[j];
11         ++j;++i;
12         f[i]=P[i]==P[j]?f[j]:j;
13     }
14 }
15 int find(char *T,char *P,int *f)
16 {
17     int n=strlen(T),m=strlen(P),ans=0;
18     getFail(P,f);
19     int i=0,j=0;
20     while(i<n)
21     {
22         while(j>=0&&P[j]!=T[i])    j=f[j];
23         ++i,++j;
24         if(j==m)    ans++;//本來還有一句j=f[j],但這句可以去掉,因為此時j==m,P[j]==‘\0‘,一定不等於T[i],一定執行j=f[j]
25     }
26     return ans;
27 }
28 int main()
29 {
30     int t;
31     scanf("%d",&t);
32     while(t--)
33     {
34         scanf("%s%s",s,s1);
35         printf("%d\n",find(s1,s,f2));
36     }
37     return 0;
38 }

為什麽當s[i]!=s[j]時,j=f[j]:

http://blog.csdn.net/qq_30974369/article/details/74276186

http://blog.csdn.net/yutianzuijin/article/details/11954939/

http://blog.csdn.net/wangbaochu/article/details/50687160

(要求從next[i]一段的末尾找到一段k,使得k和(開頭的next[i]開頭的)一段相同。也就是在next[i]的一段中找出開頭和結尾相等的一段。(根據第2、3個博客的圖))

技術分享

將各個段從左到右編號為1(灰)、2(紫)、3(紅)、4(灰)、5(綠)、6(黑)、7(灰)、8(紅)、9(灰)、10(藍)、11(黑)。

現在的情況是要尋找1-10這大段的最長公共前後綴。用f[n]表示1-n這一段的最長公共前後綴(或其長度),s[n]表示n位置的值。

如果s[5]==s[10],那麽顯然1-10的公共前後綴可以是f[9]的後面加上一個10。

可以證明這就是1-10的最長公共前後綴:如果不是,也就是f[10]>f[9]+1,也就是f[10]-1>f[9],那麽將f[10]刪除尾部的s[10],剩下長度為f[10]-1,那麽剩下的部分是1-9的公共前後綴,且其長度大於f[9],矛盾。

如果s[5]!=s[10],那麽就是要在1-9中找出1-x使得1-x一段是1-9的公共前後綴且s[x+1]==s[10]。

先考慮第一個條件,再去滿足第二個:那麽也就是要找出1段和9段相同且不等於1-4段。由於1-4段和7-9段是1-9段的公共前後綴,那麽1-4段==7-9段,也就是可以在7-9段中找到7段對應1段,在1-4段中找到4段對應9段。可以知道1、4、7、9段都相等。

由於1、4段相等,1、4段也是1-4這一段的公共前後綴。因此可以直接找f[4],結果也就是1-9段的次長公共前後綴。如果還是不行,就找f[f[4]],f[f[f[4]]],...

洛谷 P3375 【模板】KMP字符串匹配 || HDU 1686 Oulipo || kmp