zoukankan      html  css  js  c++  java
  • KMP专题

    1、HDU 1711 Number Sequence

      题意:给出两个数字串a,b,问能否使得数字串b为a的子串,能则输出最小的匹配位置。

      思路:KMP模板题

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 1000010;
     5 const int maxm = 10010;
     6 int p[maxm];
     7 int des[maxn];
     8 int Next[maxm];
     9 int n, m;
    10 void GetNext(int *next, int *src, int len)
    11 {//next和src都从下标1开始存储
    12     next[1] = 0;
    13     int j = 1, i = 0;
    14     while (j <= len)
    15     {
    16         if (i == 0 || src[j] == src[i])
    17         {
    18             j++;
    19             i++;
    20             next[j] = i;
    21         }
    22         else i = next[i];
    23     }
    24 }
    25 
    26 int KMP(int *T, int *P, int *next)//在目标串T中从位置1开始查找模式串P第一次出现的位置
    27 {
    28     int lenT = n;
    29     int lenP = m;
    30     int posT = 1, posP = 1;
    31     while (posP <= lenP&&posT <= lenT)
    32     {
    33         if (posP == 0 || T[posT] == P[posP])
    34         {
    35             ++posT;
    36             ++posP;
    37         }
    38         else posP = next[posP];
    39     }
    40     if (posP <= lenP) return -1;
    41     else return posT - lenP;
    42 }
    43 int main()
    44 {
    45     int t;
    46     scanf("%d", &t);
    47 
    48     while (t--)
    49     {
    50         scanf("%d%d", &n, &m);
    51         for (int i = 1; i <= n; i++) scanf("%d", &des[i]);
    52         for (int i = 1; i <= m; i++) scanf("%d", &p[i]);
    53         GetNext(Next, p, m);
    54         printf("%d
    ", KMP(des, p, Next));
    55     }
    56     return 0;
    57 }
    View Code

    2、HDU 1686 Oulipo

      题意:给出模式串p,文本串t,求p在t中出现的次数(可重叠)

      思路:KMP同时记录匹配的个数。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 1000100;
     5 const int maxp = 10010;
     6 char t[maxn];
     7 char p[maxp];
     8 int Next[maxp];
     9 void GetNext(int *next, char *src, int len)
    10 {//next和src都从下标0开始存储
    11     next[0] = -1;
    12     int j = 0, i = -1;
    13     while (j < len)
    14     {
    15         if (i == -1 || src[j] == src[i])
    16         {
    17             j++;
    18             i++;
    19             next[j] = i;
    20         }
    21         else i = next[i];
    22     }
    23 }
    24 
    25 int KMP(char*T, char *P, int *next)//在目标串T中从位置1开始查找模式串P第一次出现的位置
    26 {
    27     int lenT = strlen(T);
    28     int lenP = strlen(P);
    29     int posT = 0, posP = 0;
    30     int cnt = 0;
    31     while (posT <lenT)
    32     {
    33         if (posP == -1 || T[posT] == P[posP])
    34         {
    35             ++posT;
    36             ++posP;
    37         }
    38         else posP = next[posP];
    39         if (posP== lenP)
    40         {
    41             cnt++;
    42             posP = next[posP];
    43         }
    44     }
    45     return cnt;
    46 }
    47 int main()
    48 {
    49     int Case;
    50     scanf("%d", &Case);
    51     while (Case--)
    52     {
    53         scanf("%s", p);
    54         scanf("%s", t);
    55         GetNext(Next, p, strlen(p));
    56         printf("%d
    ", KMP(t,p, Next));
    57     }
    58     return 0;
    59 }
    View Code

    3、hdu 2087 剪花布条

      题意:给出a串和b串,求b串在a串中出现的次数(不可重叠)

      思路:KMP。注意更新指针时和上一题的不同。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 1010;
     5 const int maxp = 1010;
     6 char t[maxn];
     7 char p[maxp];
     8 int Next[maxp];
     9 void GetNext(int *next, char *src, int len)
    10 {//next和src都从下标0开始存储
    11     next[0] = -1;
    12     int j = 0, i = -1;
    13     while (j < len)
    14     {
    15         if (i == -1 || src[j] == src[i])
    16         {
    17             j++;
    18             i++;
    19             next[j] = i;
    20         }
    21         else i = next[i];
    22     }
    23 }
    24 
    25 int KMP(char*T, char *P, int *next)//在目标串T中从位置1开始查找模式串P第一次出现的位置
    26 {
    27     int lenT = strlen(T);
    28     int lenP = strlen(P);
    29     int posT = 0, posP = 0;
    30     int cnt = 0;
    31     while (posT <lenT)
    32     {
    33         if (posP == -1 || T[posT] == P[posP])
    34         {
    35             ++posT;
    36             ++posP;
    37         }
    38         else posP = next[posP];
    39         if (posP == lenP)
    40         {
    41             cnt++;
    42             posP++;
    43         }
    44     }
    45     return cnt;
    46 }
    47 int main()
    48 {
    49     while (~scanf("%s",&t))
    50     {
    51         if (t[0] == '#')break;
    52         scanf("%s", p);
    53         GetNext(Next, p, strlen(p));
    54         printf("%d
    ", KMP(t, p, Next));
    55     }
    56     return 0;
    57 }
    View Code

     4、HDU 3746 Cyclic Nacklace

      题意:求最少在结尾补几个字符才能使得此串形成循环.

      思路:lenB%(lenB-Next[lenB])==0则其有周期lenB/(lenB-Next[lenB]),其中最小循环节长度是lenB-Next[lenB]。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 100010;
     5 const int maxp = 100010;
     6 char t[maxn];
     7 char p[maxp];
     8 int Next[maxp];
     9 void GetNext(int *next, char *src, int len)
    10 {//next和src都从下标0开始存储
    11     next[0] = -1;
    12     int j = 0, i = -1;
    13     while (j < len)
    14     {
    15         if (i == -1 || src[j] == src[i])
    16         {
    17             j++;
    18             i++;
    19             next[j] = i;
    20         }
    21         else i = next[i];
    22     }
    23 }
    24 int main()
    25 {
    26     int C;
    27     scanf("%d", &C);
    28     while (C--)
    29     {
    30         scanf("%s", &t);
    31         int ll = strlen(t);
    32         GetNext(Next, t,ll);
    33         int len = ll - Next[ll];//循环节长度
    34         if (len != ll&&ll%len == 0) printf("0
    ");
    35         else printf("%d
    ", len - Next[ll] % len);
    36     }
    37     return 0;
    38 }
    View Code

    5、POJ 1961 Period

      题意:求出母串的所有前缀的循环节大于1的循环串。

      思路:根据next数组的性质即 i-next[i] 为一个循环节的长度。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 1000010;
     5 char des[maxn];
     6 int Next[maxn];
     7 int n, Case;
     8 void GetNext(int *next, char *src, int len)
     9 {
    10     next[0] = -1;
    11     int j = 0, i = -1;
    12     while (j <len)
    13     {
    14         if (i == -1 || src[j] == src[i])
    15         {
    16             j++;
    17             i++;
    18             next[j] = i;
    19         }
    20         else i = next[i];
    21     }
    22 }
    23 void Solve()
    24 {
    25     printf("Test case #%d
    ", Case++);
    26     for (int i = 1; i <= n; i++)
    27     {
    28         int len = i - Next[i];
    29         if (i!= len && i % len == 0)
    30         {
    31             printf("%d %d
    ", i, i / len);
    32         }
    33     }
    34     printf("
    ");
    35 }
    36 int main()
    37 {
    38     Case = 1;
    39     while (~scanf("%d", &n) && n)
    40     {
    41         scanf("%s", des);
    42         GetNext(Next, des, n);
    43         Solve();
    44     }
    45     return 0;
    46 }
    View Code

    6、HUST 1010 The Minimum Length

      题意:有一个字符串A,一次次的重写A,会得到一个新的字符串AAAAAAAA.....,现在将这个字符串从中切去一部分得到一个字符串B,例如有一个字符串A="abcdefg".,复制几次之后得到abcdefgabcdefgabcdefgabcdefg....,现在切取一部分,得到字符串B,现在只给出字符串B,求出字符串A的长度。

      思路:最小的循环节:len-next[len]。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 1000100;
     5 char des[maxn];
     6 int Next[maxn];
     7 int n, Case;
     8 void GetNext(int *next, char *src, int len)
     9 {
    10     next[0] = -1;
    11     int j = 0, i = -1;
    12     while (j <len)
    13     {
    14         if (i == -1 || src[j] == src[i])
    15         {
    16             j++;
    17             i++;
    18             next[j] = i;
    19         }
    20         else i = next[i];
    21     }
    22 }
    23 void Solve()
    24 {
    25     printf("%d
    ",n-Next[n]);
    26 }
    27 int main()
    28 {
    29     while (~scanf("%s",des))
    30     {
    31         n = strlen(des);
    32         GetNext(Next, des, n);
    33         Solve();
    34     }
    35     return 0;
    36 }
    View Code

    7、poj 2406 Power Strings

      题意:给一个字符串a,它可以看成若干个相同的子串循环形成的,求最大循环次数n。

      思路:利用KMP算法,求字符串的特征向量next,若len可以被len - next[len]整除,则最大循环次数n为len/(len - next[len]),否则为1。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 1000100;
     5 char des[maxn];
     6 int Next[maxn];
     7 int n, Case;
     8 void GetNext(int *next, char *src, int len)
     9 {
    10     next[0] = -1;
    11     int j = 0, i = -1;
    12     while (j <len)
    13     {
    14         if (i == -1 || src[j] == src[i])
    15         {
    16             j++;
    17             i++;
    18             next[j] = i;
    19         }
    20         else i = next[i];
    21     }
    22 }
    23 void Solve()
    24 {
    25     if(n%(n-Next[n])==0)printf("%d
    ", n/(n - Next[n]));
    26     else printf("1
    ");
    27 }
    28 int main()
    29 {
    30     while (~scanf("%s", des))
    31     {
    32         if (des[0] == '.')break;
    33         n = strlen(des);
    34         GetNext(Next, des, n);
    35         Solve();
    36     }
    37     return 0;
    38 }
    View Code

    8、poj 2752 Seek the Name, Seek the Fame

      题意:给出一个字符串str,求出str中存在多少子串,使得这些子串既是str的前缀,又是str的后缀。从小到大依次输出这些子串的长度。

      思路:next[i]的意义就是:前面长度为i的字串的【前缀和后缀的最大匹配长度】。所以对于这道题,求出len处的next值,并递归的向下求出所有的next值,得到的就是答案。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 400100;
     5 char des[maxn];
     6 int Next[maxn];
     7 int n, Case;
     8 void GetNext(int *next, char *src, int len)
     9 {
    10     next[0] = -1;
    11     int j = 0, i = -1;
    12     while (j <len)
    13     {
    14         if (i == -1 || src[j] == src[i])
    15         {
    16             j++;
    17             i++;
    18             next[j] = i;
    19         }
    20         else i = next[i];
    21     }
    22 }
    23 void Solve(int x)
    24 {
    25     if (x != 0)
    26     {
    27         Solve(Next[x]);
    28         if (x == n) printf("%d
    ", x);
    29         else printf("%d ", x);
    30     }
    31 }
    32 int main()
    33 {
    34     while (~scanf("%s", des))
    35     {
    36         n = strlen(des);
    37         GetNext(Next, des, n);
    38         Solve(n);
    39     }
    40     return 0;
    41 }
    View Code

    9、POJ 3080 Blue Jeans

      题意:找出n个串中最长的公共串,并且要求字典序最大。

      思路:直接枚举第一串的所有子串,然后与后面的所有串进行比较即可。

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<string.h>
     4 using namespace std;
     5 
     6 const int N = 107;
     7 char s[N][61];
     8 int Next[N];
     9 
    10 void GetNext(char a[], int n)
    11 {
    12     int i = 0, j = -1;
    13     Next[0] = -1;
    14     while (i<n)
    15     {
    16         if (j == -1 || a[i] == a[j])
    17             Next[++i] = ++j;
    18         else
    19             j = Next[j];
    20     }
    21 }
    22 
    23 int kmp(char a[], char b[], int n)
    24 {
    25     int i = 0, j = 0;
    26     while (i<60)
    27     {
    28 
    29         if (j == -1 || b[i] == a[j])
    30             i++, j++;
    31         else
    32             j = Next[j];
    33         if (j == n)
    34             return true;
    35     }
    36     return false;
    37 }
    38 
    39 int main()
    40 {
    41     int T, n, f;
    42     scanf("%d", &T);
    43     while (T--)
    44     {
    45         char ans[N] = "Z";
    46         scanf("%d", &n);
    47         for (int i = 0; i<n; i++)
    48             scanf("%s", s[i]);
    49         for (int len = 60; len >= 3; len--)//子串的长度
    50         {
    51             for (int i = 0; i <= 60 - len; i++)//子串的开始下标
    52             {
    53                 char subStr[N] = { 0 };
    54                 strncpy(subStr, s[0] + i, len);
    55 
    56                 GetNext(subStr, len);//从subStr中得到Next
    57                 int flag = 0;
    58                 for (int j = 1; j<n; j++)
    59                 {
    60                     if (!kmp(subStr, s[j], len))
    61                     {
    62                         flag = 1;
    63                         break;
    64                     }
    65                 }
    66                 if (flag == 0 && strcmp(ans, subStr)>0)
    67                     strcpy(ans, subStr);
    68             }
    69             f = 0;
    70             if (ans[0] != 'Z')
    71             {
    72                 printf("%s
    ", ans);
    73                 f = 1;
    74                 break;
    75             }
    76         }
    77         if (f == 0)
    78             printf("no significant commonalities
    ");
    79 
    80     }
    81     return 0;
    82 }
    View Code

    10、HDU 2594 Simpsons’ Hidden Talents

      题意:给定两个字符串s1,s2,求最长的s1前缀ss使得ss为s2的后缀,输出该字符串和其长度。

      思路:①将s1和s2拼接在一起,next[len]也就是最终答案,不过需要注意的是next[len]不能大于s1,s2的长度。

           ②题目要求最长的s1的前缀同时满足是s2的后缀,可以反过来想一下,把s2作为主串,s1为子串,然后s1不断往后移动,匹配之后就可以了.

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn = 50010*2;
     5 const int maxp = 50010*2;
     6 char t[maxn];
     7 char p[maxp];
     8 int Next[maxp];
     9 void GetNext(int *next, char *src, int len)
    10 {//next和src都从下标0开始存储
    11     next[0] = -1;
    12     int j = 0, i = -1;
    13     while (j < len)
    14     {
    15         if (i == -1 || src[j] == src[i])
    16         {
    17             j++;
    18             i++;
    19             next[j] = i;
    20         }
    21         else i = next[i];
    22     }
    23 }
    24 
    25 int KMP(char*T, char *P, int *next)//在目标串T中从位置1开始查找模式串P第一次出现的位置
    26 {
    27     int lenT = strlen(T);
    28     int lenP = strlen(P);
    29     int posT = 0, posP = 0;
    30     while (posT <lenT)
    31     {
    32         if (posP == -1 || T[posT] == P[posP])
    33         {
    34             ++posT;
    35             ++posP;
    36         }
    37         else posP = next[posP];
    38     }
    39     return posP;
    40 }
    41 int main()
    42 {
    43     while (~scanf("%s", &p))
    44     {
    45         scanf("%s", t);
    46         int lt = strlen(t), lp = strlen(p);
    47         strcat(p, t);//新增
    48         GetNext(Next, p, strlen(p));
    49         //int ll=KMP(t, p, Next);
    50         int ll = lt+lp;
    51         while (Next[ll] > lt || Next[ll] > lp) ll = Next[ll];
    52         ll = Next[ll];
    53         if (ll>0)
    54         {
    55             for (int i = 0; i < ll; i++) printf("%c", p[i]);
    56             printf(" %d
    ", ll);
    57         }
    58         else printf("0
    ");
    59     }
    60     return 0;
    61 }
    View Code

    11、hdu 3336 Count the string

      题意:给出s,求所有s的前缀在s中出现的次数的和。

      思路:因为next[i]表示前i个字符所组成的字符串的最大前后缀匹配长度 

        ①不断递归求得next[i],cnt++.

        ②所求=字串的前缀个数+与前缀相同的子串。不断累加next[i](当next[i]!=0&&next[i+1]!=next[i])

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxp = 200010;
     5 const int MOD = 10007;
     6 char p[maxp];
     7 int Next[maxp];//next数组的值就是当前子串的后缀与前缀匹配的个数
     8 void GetNext(int *next, char *src, int len)
     9 {//next和src都从下标0开始存储
    10     next[0] = -1;
    11     int j = 0, i = -1;
    12     while (j < len)
    13     {
    14         if (i == -1 || src[j] == src[i])
    15         {
    16             j++;
    17             i++;
    18             next[j] = i;
    19         }
    20         else i = next[i];
    21     }
    22 }
    23 
    24 int main()
    25 {
    26     int t;
    27     scanf("%d", &t);
    28     while (t--)
    29     {
    30         int lp;
    31         scanf("%d", &lp);
    32         scanf("%s", p);
    33         GetNext(Next, p, lp);
    34         int ans = lp%MOD;
    35         for (int i = 1; i <= lp; i++)
    36         {
    37             int tmp = i;
    38             while (Next[tmp]) ans = (ans + 1) % MOD, tmp = Next[tmp];
    39         }
    40         printf("%d
    ", ans);
    41     }
    42     return 0;
    43 }
    View Code

     12、HDU 4300 Clairewds message

      题意:给你一个加密协议,即26个英文字母加密对应表,然后给你一个前一段是加密串,后一段为加密串对应的原串的字符串(原串可能小于加密串)输出最短的原串和加密串。

      思路:先将原串S全部反加密得到串S',再将S'串去匹配S,得到明文的长度。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <string>
     4 #include <cstdlib>
     5 #include <iostream>
     6 #include <algorithm>
     7 using namespace std;
     8 const int MAXN = 100017;
     9 
    10 char strm[MAXN];//密码表
    11 char strz[MAXN];//不完整的密文和密文
    12 char str[MAXN]; //密码表转换后
    13 char s[MAXN];   //密文和明文转换后
    14 int Next[MAXN];
    15 
    16 void getnext(char T[], int len)
    17 {
    18     int i = 0, j = -1;
    19     Next[0] = -1;
    20     while (i < len)
    21     {
    22         if (j == -1 || T[i] == T[j])
    23         {
    24             i++, j++;
    25             Next[i] = j;
    26         }
    27         else
    28             j = Next[j];
    29     }
    30 }
    31 int KMP(int len1, int len2)
    32 {
    33     int i, j = 0;
    34     if (len1 % 2 == 1)
    35     {
    36         i = len1 / 2 + 1;
    37     }
    38     else
    39         i = len1 / 2;
    40     while (i < len1 && j < len2)
    41     {
    42         if (j == -1 || strz[i] == s[j])
    43         {
    44             i++, j++;
    45         }
    46         else
    47             j = Next[j];
    48     }
    49     return j;//j值代表的就是前缀和后缀的最长相等长度
    50 }
    51 
    52 int main()
    53 {
    54     int t;
    55     scanf("%d", &t);
    56     while (t--)
    57     {
    58         scanf("%s", strm);
    59         for (int i = 0; i < 26; i++)
    60         {
    61             char ss = strm[i];
    62             str[ss] = i;
    63         }
    64         scanf("%s", strz);
    65         int lenz = strlen(strz);
    66         for (int i = 0; i < lenz; i++)//将密文和明文按照密码表转换
    67         {
    68             int tt = str[strz[i]];
    69             s[i] = 'a' + tt;
    70         }
    71         int lens = strlen(s);
    72         getnext(s, lens);
    73         int j = KMP(lenz, lens);//得到密文的个数
    74         if (j * 2 == lenz)//前缀和后缀恰各占一半
    75         {
    76             printf("%s
    ", strz);
    77         }
    78         else
    79         {
    80             int tt = lenz - j;
    81             printf("%s", strz);
    82             for (int i = j; i < tt; i++)//需要添加的明文
    83             {
    84                 printf("%c", s[i]);
    85             }
    86             printf("
    ");
    87         }
    88     }
    89     return 0;
    90 }
    View Code

     13、POJ 3450 Corporate Identity

      题意:求n个字符串的最长公共字串。

      思路:选取其中一个字符串,枚举其所有后缀,KMP求得其后缀和其余字符串的最长前缀的最小值,更新ans.

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<string.h>
     5 using namespace std;
     6 const int maxn = 4010;
     7 const int maxl = 210;
     8 char str[maxn][maxl];
     9 int Next[maxl];
    10 char tstr1[maxl], tstr2[maxl];
    11 int n;
    12 void GetNext(int *next, char *src, int len)
    13 {//next和src都从下标0开始存储
    14     next[0] = -1;
    15     int j = 0, i = -1;
    16     while (j < len)
    17     {
    18         if (i == -1 || src[j] == src[i])
    19         {
    20             j++;
    21             i++;
    22             next[j] = i;
    23         }
    24         else i = next[i];
    25     }
    26 }
    27 
    28 int KMP(char*T, char *P, int *next)
    29 {//查找字符串P在字符串T的最长前缀子字符串
    30     int lenT = strlen(T);
    31     int lenP = strlen(P);
    32     int posT = 0, posP = 0;
    33     int cnt = 0;
    34     int maxmatch = 0;
    35     while (posP<lenP&&posT <lenT)
    36     {
    37         if (posP == -1 || T[posT] == P[posP])
    38         {
    39             ++posT;
    40             ++posP;
    41             maxmatch = max(maxmatch, posP);
    42         }
    43         else posP = next[posP];
    44     }
    45     return maxmatch;
    46 }
    47 int main()
    48 {
    49     while (~scanf("%d", &n) && n)
    50     {
    51         for (int i = 1; i <= n; i++) scanf("%s", str[i]);
    52         int len = strlen(str[1]);
    53         int ans = 0, pos = 0;
    54         for (int i = 0; i < len; i++)
    55         {//遍历第一个字符串的所有后缀p
    56             GetNext(Next, str[1] + i, len - i);
    57             int tl = maxl;
    58             for (int j = 2; j <= n; j++)
    59             {//得到后缀p和剩余n-1个字符串的相同前缀的最小值
    60                 tl = min(tl, KMP(str[j], str[1] + i, Next));
    61             }
    62             if (tl > ans) ans = tl, pos = i;
    63             else if (tl == ans)
    64             {//得到字典序较小的
    65                 memset(tstr1, 0, sizeof(tstr1));
    66                 memset(tstr2, 0, sizeof(tstr2));
    67 
    68                 strncpy(tstr1, str[1] + pos, ans);
    69                 strncpy(tstr2, str[1]+i,ans);
    70                 if (strcmp(tstr2, tstr1) < 0) pos = i;
    71             }
    72         }
    73         if (ans > 0)
    74         {
    75             memset(tstr1, 0, sizeof(tstr1));
    76             strncpy(tstr1, str[1] + pos, ans);
    77             printf("%s
    ", tstr1);
    78         }
    79         else printf("IDENTITY LOST
    ");
    80     }
    81     return 0;
    82 }
    83 /*
    84 4
    85 abcd
    86 abcd
    87 abcd
    88 abcd
    89 3
    90 abc
    91 abcc
    92 ab
    93 
    94 */
    View Code

     14、HDU 1238 Substrings

      题意:找到一个子串,该子串的顺或逆为所有字符串的子串,求其最长长度。

      思路:枚举子串,和其他字符串匹配。

      1 #include<iostream>  
      2 #include<cstdio>  
      3 #include<cstring>  
      4 #include<cstdlib>  
      5 #include<algorithm>  
      6 using namespace std;
      7 const int inf = 0x3f3f3f3f;
      8 const int mod = 1000000007;
      9 const int maxn = 500005;
     10 typedef long long ll;
     11 void get_next(char *s, int *Next)
     12 {
     13     int len = strlen(s);
     14     Next[0] = -1;
     15     int index;
     16     for (int i = 1; i<len; ++i)
     17     {
     18         index = Next[i - 1];
     19         while (index >= 0 && s[i] != s[index + 1])
     20         {
     21             index = Next[index];
     22         }
     23         if (s[i] == s[index + 1])
     24         {
     25             Next[i] = index + 1;
     26         }
     27         else
     28         {
     29             Next[i] = -1;
     30         }
     31     }
     32 }
     33 int KMP(char *target, char *pattern)
     34 {
     35     int len1 = strlen(target);
     36     int len2 = strlen(pattern);
     37     int Next[maxn];
     38     get_next(pattern, Next);
     39     int i = 0, j = 0;
     40     while (i < len1 && j < len2)
     41     {
     42         if (target[i] == pattern[j])
     43         {
     44             ++i;
     45             ++j;
     46         }
     47         else if (j == 0)
     48         {
     49             ++i;
     50         }
     51         else
     52         {
     53             j = Next[j - 1] + 1;
     54         }
     55     }
     56     if (j == len2)
     57     {
     58         return i - j;
     59     }
     60     else
     61     {
     62         return -1;
     63     }
     64 }
     65 char str[101][101];
     66 int main()
     67 {
     68     int n, t, k;
     69     char s1[101], s2[101];
     70     scanf("%d", &t);
     71     while (t--)
     72     {
     73         scanf("%d", &n);
     74         int pos = 0, MIN = inf;
     75         int flag = 0;
     76         for (int i = 0; i<n; ++i)
     77         {
     78             scanf("%s", str[i]);
     79             int len = strlen(str[i]);
     80             if (len < MIN)
     81             {
     82                 MIN = len;
     83                 pos = i;
     84             }
     85         }
     86         for (int i = MIN; i>0; --i)
     87         {//枚举子串长度,从大开始  
     88             for (int j = 0; j + i <= MIN; ++j)
     89             {//枚举子串起点  
     90                 for (k = 0; k<i; ++k)
     91                 {//生成子串和反串  
     92                     s1[k] = str[pos][k + j];
     93                     s2[i - k - 1] = str[pos][k + j];
     94                 }
     95                 s1[i] = 0;
     96                 s2[i] = 0;
     97                 for (k = 0; k<n; ++k)
     98                 {//匹配  
     99                     if (KMP(str[k], s1) == -1 && KMP(str[k], s2) == -1)
    100                     {
    101                         break;
    102                     }
    103                 }
    104                 if (k == n)
    105                 {
    106                     flag = 1;
    107                     printf("%d
    ", i);
    108                     break;
    109                 }
    110             }
    111             if (flag)break;
    112         }
    113         if (!flag)
    114         {
    115             printf("0
    ");
    116         }
    117     }
    118     return 0;
    119 }
    View Code

     15、HDU 3374 String Problem

      题意:求给出字符串的最小和最大表示的开始位置,并且确定其数目。

      思路:字符串最小最大表示模板。数目可通过最小循环节len-Next[len]确定。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 #define MAXN 1000100
     8 
     9 int len;
    10 int Next[MAXN];
    11 char Str[MAXN];
    12 int minTimes, maxTimes;
    13 int minRank, maxRank;
    14 
    15 //求最小表示的起始坐标
    16 int getMin()
    17 {
    18     int i = 0, j = 1, k = 0;
    19     while (i< len && j< len&&k<len)
    20     {
    21         if (Str[(i + k)%len] == Str[(j + k)%len])
    22             k++;
    23         else
    24         {
    25             if (Str[(i + k) % len] > Str[(j + k) % len])
    26                 i +=k + 1;
    27             else
    28                 j +=k+1;
    29             k = 0;
    30             if (i == j)//若滑动后i == j那么j++
    31                 j++;
    32         }
    33     }
    34     return min(i, j);
    35 }
    36 
    37 //求最大表示的起始坐标
    38 int getMax()
    39 {
    40     int i = 0, j = 1, k = 0;
    41     while (i< len && j< len&&k<len)
    42     {
    43         if (Str[(i + k)%len] == Str[(j + k)%len])
    44             k++;
    45         else
    46         {
    47             if (Str[(i + k) % len] < Str[(j + k) % len])
    48                 i = i + k + 1;
    49             else
    50                 j = j + k + 1;
    51             k = 0;
    52             if (i == j)//若滑动后i == j那么j++
    53                 j++;
    54         }
    55     }
    56     return min(i, j);
    57 }
    58 //求next数组
    59 void GetNext(int *next, char *src, int len)
    60 {//next和src都从下标0开始存储
    61     next[0] = -1;
    62     int j = 0, i = -1;
    63     while (j < len)
    64     {
    65         if (i == -1 || src[j] == src[i])
    66         {
    67             j++;
    68             i++;
    69             next[j] = i;
    70         }
    71         else i = next[i];
    72     }
    73 }
    74 int main()
    75 {
    76     while (~scanf("%s", Str))
    77     {
    78 
    79         int flag;
    80         len = strlen(Str);
    81         //求字符串的最小表示
    82         minRank = getMin();
    83         //求字符串的最大表示
    84         maxRank = getMax();
    85         GetNext(Next,Str,len);
    86         minTimes = maxTimes = (len % (len - Next[len]) ? 1 : len / (len - Next[len]));
    87         //输出
    88         printf("%d %d %d %d
    ", minRank + 1, minTimes, maxRank + 1, maxTimes);
    89     }
    90     return 0;
    91 }
    View Code

     16、hdu 2609 How many

      题意:求给出的n的字符串的不同数目。(如果一个字符串通过旋转能够得到另一个字符串,则当成相同字符串)

      思路:将所有字符串用最小表示法表示,然后set一下。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<string.h>
     4 #include<string>
     5 #include<set>
     6 using namespace std;
     7 const int maxn = 10010;
     8 const int maxl = 110;
     9 char s[maxl];
    10 char tmp[maxl];
    11 set<string>ss;
    12 int n;
    13 //求最小表示的起始坐标
    14 int getMin(char *Str,int len)
    15 {
    16     int i = 0, j = 1, k = 0;
    17     while (i< len && j< len&&k<len)
    18     {
    19         if (Str[(i + k) % len] == Str[(j + k) % len])
    20             k++;
    21         else
    22         {
    23             if (Str[(i + k) % len] > Str[(j + k) % len])
    24                 i += k + 1;
    25             else
    26                 j += k + 1;
    27             k = 0;
    28             if (i == j)//若滑动后i == j那么j++
    29                 j++;
    30         }
    31     }
    32     return min(i, j);
    33 }
    34 int Solve()
    35 {
    36     ss.clear();
    37     for (int i = 1; i <= n; i++)
    38     {
    39         scanf("%s", s);
    40         int len = strlen(s);
    41         int pos = getMin(s, len);
    42         memset(tmp, 0, sizeof(tmp));
    43         strncpy(tmp, s+pos, len - pos);
    44         strncat(tmp, s, pos);
    45         ss.insert(string(tmp));
    46     }
    47     return ss.size();
    48 }
    49 int main()
    50 {
    51     while (~scanf("%d", &n))
    52     {
    53         printf("%d
    ", Solve());
    54     }
    55     return 0;
    56 }
    View Code

     17、FZU Problem 1901 Period II

      题意:给你一个字符串str,对于每个str长度为p的前缀,如果str[i]==str[p+i](p+i<len),那么我们认为它是一个periodic prefixs.求所有满足题意的前缀的长度p。

      思路:对于模式串str,next数组的意义就是:如果next(j)=t,那么str[1…t]=str[len-t+1…len]。所以不断递归打印next[j]即可。

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 const int maxn = 1000010;
     5 char des[maxn];
     6 int Next[maxn];
     7 int ans[maxn];
     8 void GetNext(int *next, char *src, int len)
     9 {//next和src都从下标0开始存储
    10     next[0] = -1;
    11     int j = 0, i = -1;
    12     while (j < len)
    13     {
    14         if (i == -1 || src[j] == src[i])
    15         {
    16             j++;
    17             i++;
    18             next[j] = i;
    19         }
    20         else i = next[i];
    21     }
    22 }
    23 int main()
    24 {
    25     int C = 1;
    26     int t;
    27     scanf("%d", &t);
    28     while (t--)
    29     {
    30         scanf("%s", des);
    31         int len = strlen(des);
    32         GetNext(Next, des, len);
    33         int cnt = 0;
    34         int ll = Next[len];
    35         while (ll)
    36         {
    37             ans[cnt++] = len - ll;
    38             ll = Next[ll];
    39         }
    40         ans[cnt++] = len;
    41         printf("Case #%d: %d
    ", C++,cnt);
    42         for (int i = 0; i < cnt; i++)
    43         {
    44             if (i) printf(" ");
    45             printf("%d", ans[i]);
    46         }
    47         printf("
    ");
    48     }
    49     return 0;
    50 }
    View Code

     18、HDU 3613 Best Reward

      题意:给出一个由26种字母组成的字符串,每种字母有权值,当一个串是回文串的时候,它的权值是其中所有字母的权值之和,否则是0。现在要将这个字符串切成两段,问两段的权值和最大是多少。

      思路:扩展KMP模板。将原串s1反转得到s2,然后进行用s1对s2进行扩展KMP匹配, 得到extend, 对于s1的前i个字符如果和s2的后i个字符相等即extend[len - i] == i则前i个字符为回文串, 判断后len - i个字符是否是回文串用s2对s1进行扩展KMP即可

     1 //将原串s1反转得到s2,然后进行s1, s2扩展KMP匹配, 得到extend, 对于s1的前i个字符如果和s2的后i个字符相等即extend[len - i] == i则前i个字符为回文串, 判断后len - i个字符是否是回文串用s2, s1进行扩展KMP即可
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<string>
     6 #include<algorithm>
     7 #define INF 0x3f3f3f3f;
     8 using namespace std;
     9 
    10 const int MAX = 500000 + 10;
    11 char s1[MAX], s2[MAX];
    12 int Next[MAX], extend1[MAX], extend2[MAX];
    13 int sum[MAX], val[27];
    14 //extend[i]表示目标串从i开始的后缀和模式串的前缀的最长公共长度
    15 void get_next(char *P, int len)
    16 {
    17     int k = 0, i = 1;
    18     Next[0] = len;//可有可无,因为用不上 
    19     while (k + 1<len && P[k] == P[k + 1])++k;
    20     Next[1] = k;//这里预先算好next[1]是因为不能k=0,否则next[i-k]=next[i]不是已算好的 
    21     k = 1;
    22     while (++i<len)
    23     {//和EKMP的过程一样 
    24         int maxr = k + Next[k] - 1;
    25         Next[i] = min(Next[i - k], max(maxr - i + 1, 0));//这里是扩展KMP的精髓,即算法核心思想就是这
    26         while (i + Next[i]<len && P[Next[i]] == P[i + Next[i]])++Next[i];
    27         if (i + Next[i]>k + Next[k])k = i;
    28     }
    29 }
    30 
    31 void EKMP(char *P, char *T, int *extend)
    32 {
    33     int lenP = strlen(P);
    34     int lenT = strlen(T);
    35     get_next(P, lenP);
    36     int k = 0, i = 0;
    37     int len = min(lenP, lenT);
    38     while (k<len && P[k] == T[k])++k;
    39     extend[0] = k;
    40     k = 0;
    41     while (++i<lenT)
    42     {
    43         int maxr = k + extend[k] - 1;
    44         extend[i] = min(Next[i - k], max(maxr - i + 1, 0));//next[i-k]是a与b从i开始的可能已经匹配的长度
    45         while (i + extend[i]<lenT &&extend[i]<lenP&&P[extend[i]] == T[i + extend[i]])++extend[i];//这里是扩展KMP的精髓,即算法核心思想就是这
    46         if (i + extend[i]>k + extend[k])k = i;
    47         //int pp = k + extend[k] - 1, ll = Next[i - k];
    48         //if (i - 1 + ll >= pp)
    49         //{
    50         //    int j = max(0, pp - i + 1);
    51         //    while (i + j < lenT&&j < lenP&&T[i + j] == P[j]) j++;
    52         //    extend[i] = j;
    53         //    k = i;
    54         //}
    55         //else extend[i] = ll;
    56     }
    57 }
    58 
    59 int main()
    60 {
    61     int n;
    62     cin >> n;
    63     while (n--)
    64     {
    65         for (int i = 0; i<26; ++i)cin >> val[i];
    66         scanf("%s", s1);
    67         int len = strlen(s1);
    68         for (int i = 1; i <= len; ++i)
    69         {
    70             sum[i] = sum[i - 1] + val[s1[i - 1] - 'a'];//记录前缀价值和
    71             s2[i - 1] = s1[len - i];//得到原串的反转
    72         }
    73         EKMP(s1, s2, extend1);
    74         EKMP(s2, s1, extend2);
    75         int ans = 0, temp = 0;
    76         for (int i = 1; i<len; ++i)
    77         {
    78             if (extend1[len - i] == i)temp += sum[i];//表示前i个字符是回文串
    79             if (extend2[i] == len - i)temp += sum[len] - sum[i];//表示后len-i个字符为回文串
    80             if (temp>ans)ans = temp;
    81             temp = 0;
    82         }
    83         cout << ans << endl;
    84     }
    85     return 0;
    86 }
    View Code

     19、poj 3376 Finding Palindromes

      题意:给你N个字符串, 你可以两两连接得到N * N个字符串, 问之中回文串的数量. N个字符串的长度和加起来不超过2000000.

      思路:扩展KMP+字典树

    假如我们把其中一个翻转,若此时,短的那个串是长的那个的前缀,而长的那个串后面剩余的后缀恰好是个回文串,那这两个串连起来就是个回文串了。比如abc和abacba连接,我们把后一个串翻转,得到abcaba,abc为其前缀,而aba是个回文串,那么连起来就是个回文串。

    那我们就把所有的串插入到trie中,然后再用所有的反串去匹配。匹配的过程中,走到任意一个节点,而这个节点有可能是若干个串的结尾,那么此时判反串匹配位置下面剩余的部分是否回文。如果是的,ans就加上以这个节点为结尾的原串的个数(这个插入的时候就可以统计进去了)。如果走完了,还没走到叶子节点,那么就要看走到的节点下的子树(其实是以前面走过的路径为前缀的字符串剩下的一些后缀)有多少是回文的了(这个先预处理所有的串的后缀有哪些是回文的,然后在插入的时候统计到节点上)。

    ————————摘自:http://blog.csdn.net/pi9nc/article/details/12524647

      1 #include<stdio.h>  
      2 #include<algorithm>  
      3 #include<string.h>  
      4 #define ll __int64  
      5 #include<vector>  
      6 using namespace std;
      7 
      8 const int maxn = 2222222;
      9 char vec[maxn];
     10 int g[maxn], nxt[maxn];
     11 bool st[maxn];
     12 int extend[maxn], Next[maxn];
     13 void get_Next(const char *P)
     14 {
     15     int len = strlen(P);
     16     int k = 0, i = 1;
     17     Next[0] = len;//可有可无,因为用不上 
     18     while (k + 1<len && P[k] == P[k + 1])++k;
     19     Next[1] = k;//这里预先算好next[1]是因为不能k=0,否则next[i-k]=next[i]不是已算好的 
     20     k = 1;
     21     while (++i<len)
     22     {//和EKMP的过程一样 
     23         int maxr = k + Next[k] - 1;
     24         Next[i] = min(Next[i - k], max(maxr - i + 1, 0));//这里是扩展KMP的精髓,即算法核心思想就是这
     25         while (i + Next[i]<len && P[Next[i]] == P[i + Next[i]])++Next[i];
     26         if (i + Next[i]>k + Next[k])k = i;
     27     }
     28 }
     29 
     30 void EXKMP(char *T, char *P)
     31 {
     32     int lenT = strlen(T), lenP = strlen(P);
     33     int k = 0, i = 0;
     34     int len = min(lenP, lenT);
     35     while (k<len && P[k] == T[k])++k;
     36     extend[0] = k;
     37     k = 0;
     38     while (++i<lenT)
     39     {
     40         int maxr = k + extend[k] - 1;
     41         extend[i] = min(Next[i - k], max(maxr - i + 1, 0));//next[i-k]是a与b从i开始的可能已经匹配的长度
     42         while (i + extend[i]<lenT &&extend[i]<lenP&&P[extend[i]] == T[i + extend[i]])++extend[i];//这里是扩展KMP的精髓,即算法核心思想就是这
     43         if (i + extend[i]>k + extend[k])k = i;
     44     }
     45 }
     46 
     47 int tot = 0, c[maxn][26], cnt[maxn], val[maxn];
     48 //cnt[i]记录从i到叶子有多少回文串;val[i]表示在该点结束的字符串的个数
     49 int new_node()
     50 {
     51     int i;
     52     for (i = 0; i < 26; i++) c[tot][i] = 0;
     53     cnt[tot] = val[tot] = 0;
     54     return tot++;
     55 }
     56 
     57 void insert(char *s)
     58 {
     59     int len = strlen(s), i, now = 0;
     60     for (i = 0; i < len; i++)
     61     {
     62         int k = s[i] - 'a';
     63         if (!c[now][k]) c[now][k] = new_node();
     64         now = c[now][k];
     65         if (i + 1 < len && extend[i + 1] == len - i - 1)
     66         {//s[i+1]~s[len-1]为回文串
     67             cnt[now] ++;
     68         }
     69     }
     70     cnt[now] ++;
     71     val[now] ++;
     72 }
     73 
     74 ll ans = 0;
     75 
     76 void cal(int len)
     77 {
     78     int j, i, now = 0;
     79     st[len + 1] = 1;
     80     for (j = 1; j <= len; j++)
     81     {
     82         if (st[j]) now = 0;
     83         int k = vec[j] - 'a';
     84         if (!c[now][k])
     85         {
     86             now = 0;
     87             j = nxt[j] - 1;
     88             continue;
     89         }
     90         now = c[now][k];
     91         if (!st[j + 1] && g[j + 1]) ans += (ll)val[now];
     92         if (st[j + 1])
     93         {
     94             ans += (ll)cnt[now];
     95             now = 0;
     96         }
     97     }
     98 }
     99 
    100 char s1[maxn], s[maxn];
    101 
    102 int main()
    103 {
    104     int n, i, j, k;
    105     while (~scanf("%d", &n))
    106     {
    107         tot = 0;
    108         new_node();//建立Tire根结点
    109         int t = 0;
    110         for (i = 1; i <= n; i++)
    111         {
    112             scanf("%d%s", &j, s);
    113             strcpy(s1, s);
    114             int len = strlen(s);
    115             reverse(s1, s1 + len);//反转
    116             get_Next(s1);
    117             EXKMP(s, s1);//s1匹配s,得到s后缀回文串的长度
    118             insert(s);//插入原串
    119             get_Next(s);
    120             EXKMP(s1, s);//s匹配s1,得到s前缀回文串的长度
    121             st[t + 1] = 1;//起点
    122             for (j = 0; j < len; j++)
    123             {
    124                 if (extend[j] == len - j) g[++t] = 1;//如果s[0]~s[j]为回文串,则标记
    125                 else g[++t] = 0;
    126                 vec[t] = s1[j];//记录反转串
    127                 if (j) st[t] = 0;
    128             }
    129         }
    130         int last = t + 1;//最后一个反转串的下一个,结束标志
    131         for (i = t; i >= 1; i--)
    132         {
    133             nxt[i] = last;//记录当前反转串的下一个反转串
    134             if (st[i]) last = i;
    135         }
    136         ans = 0;
    137         cal(t);
    138         printf("%I64d
    ", ans);
    139     }
    140     return 0;
    141 }
    View Code

    20、hdu 4763 Theme Section

      题意:找到给出字符串的一个子串,其是该字符串的前缀和后缀,且在字符串中间出现过(和前缀后缀不重合),求其最大长度。

      思路:根据next数组性质,递归找next[len],判断中间是否出现过。

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxp = 1000010;
     5 char p[maxp];
     6 int Next[maxp];
     7 void GetNext(int *next, char *src, int len)
     8 {//next和src都从下标0开始存储
     9     next[0] = -1;
    10     int j = 0, i = -1;
    11     while (j < len)
    12     {
    13         if (i == -1 || src[j] == src[i])
    14         {
    15             j++;
    16             i++;
    17             next[j] = i;
    18         }
    19         else i = next[i];
    20     }
    21 }
    22 int main()
    23 {
    24     int n;
    25     scanf("%d", &n);
    26     for (int i = 1; i <= n; i++)
    27     {
    28         scanf("%s", p);
    29         int len = strlen(p);
    30         GetNext(Next, p, len);
    31         int ll = Next[len];
    32         int ans = 0;
    33         while (ll > 0)
    34         {
    35             bool can = false;
    36             for (int i = ll; i < len - ll; i++)
    37             {
    38                 if (p[i] == p[0])
    39                 {
    40                     int j = i, k = 0;
    41                     while (k<ll&&p[j] == p[k]) j++, k++;
    42                     if (k == ll)
    43                     {
    44                         can = true;
    45                         break;
    46                     }
    47                 }
    48             }
    49             if (can)
    50             {
    51                 ans = ll;
    52                 break;
    53             }
    54             ll = Next[ll];
    55         }
    56         printf("%d
    ", ans);
    57     }
    58     return 0;
    59 }
    View Code
  • 相关阅读:
    手机键盘
    关于大数除法
    整数转字符串,字符串转整数
    动态规划习题:数字三角形(01背包)
    关于最长不重复子串的问题
    Ubuntu12.04 下vim的使用(不断更新)
    Ubuntu12.04 如何设置环境变量
    LeetCode--Reverse Linked List II
    Ubuntu12.04 使用svn如何设置代理
    July面试整理系列--(5)
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7407228.html
Copyright © 2011-2022 走看看