• [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher 题解报告


    来刷kuangbin字符串了,字符串处理在ACM中是很重要的,一般比赛都会都1——2道有关字符串处理的题目,而且不会很难的那种,大多数时候都是用到一些KMP的性质或者找规律。

    点击标题可跳转至VJ比赛题目链接。

    A - Number Sequence

    题意就是让你去找在串A找串B首次出现的位置,现在串不是字符串,而是数字串,所以用int数组存储即可,然后就是裸KMP。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 
     7 int n,m;
     8 int a[1000005],b[10005];
     9 int nxt[1000005];
    10 
    11 void getnext(){
    12     nxt[0] = -1;
    13     int i = 0, j = -1;
    14     while(i < n){
    15         if(j == -1 || b[i] == b[j])
    16             nxt[++i] = ++j;
    17         else
    18             j = nxt[j];
    19     }
    20 }
    21 
    22 int KMP(){
    23     getnext();
    24     int i = 0,j = 0;
    25     while(i < n){
    26         if(j == -1 || a[i] == b[j])
    27             ++i,++j;
    28         else
    29             j = nxt[j];
    30         if(j == m)
    31             return i-m+1;
    32     }
    33     return -1;
    34 }
    35 
    36 int main(){
    37     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    38     int t;
    39     cin>>t;
    40     while(t--){
    41         cin>>n>>m;
    42         for(int i = 0; i < n; i++)
    43             cin>>a[i];
    44         for(int i = 0; i < m; i++)
    45             cin>>b[i];
    46         cout << KMP() << endl;
    47     }
    48 
    49     return 0;
    50 }

    B - Oulipo

    题意就是找字符串A在字符串B中出现的次数,还是裸KMP,当j指针等于字符串A长度时,次数加一即可。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 
     7 string a,b;
     8 int nxt[1000005];
     9 
    10 void getnext(){
    11     nxt[0] = -1;
    12     int i = 0, j = -1,len = b.size();
    13     while(i < len){
    14         if(j == -1 || b[i] == b[j])
    15             nxt[++i] = ++j;
    16         else
    17             j = nxt[j];
    18     }
    19 }
    20 
    21 int KMP(){
    22     getnext();
    23     int i = 0,j = 0,sum = 0,len1 = a.size(),len2 = b.size();
    24     while(i < len1){
    25         if(j == -1 || a[i] == b[j])
    26             ++i,++j;
    27         else
    28             j = nxt[j];
    29         if(j == len2)
    30             sum++;
    31     }
    32     return sum;
    33 }
    34 
    35 int main(){
    36     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    37     int t;
    38     cin>>t;
    39     while(t--){
    40         cin>>b>>a;
    41         cout << KMP() << endl;
    42     }
    43 
    44     return 0;
    45 }

    C - 剪花布条

    同上一题,不过这题被匹配过的串就不能在被匹配了,所以使next数组中next[len] = 0,即当j等于len时候,从0开始重新匹配。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 
     7 string a,b;
     8 int nxt[1000005];
     9 
    10 void getnext(){
    11     nxt[0] = -1;
    12     int i = 0, j = -1,len = b.size();
    13     while(i < len){
    14         if(j == -1 || b[i] == b[j])
    15             nxt[++i] = ++j;
    16         else
    17             j = nxt[j];
    18     }
    19     nxt[len] = 0;
    20 }
    21 
    22 int KMP(){
    23     getnext();
    24     int i = 0,j = 0,sum = 0,len1 = a.size(),len2 = b.size();
    25     while(i < len1){
    26         if(j == -1 || a[i] == b[j])
    27             ++i,++j;
    28         else
    29             j = nxt[j];
    30         if(j == len2)
    31             sum++;
    32     }
    33     return sum;
    34 }
    35 
    36 int main(){
    37     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    38     while(cin>>a && a[0] != '#'){
    39         cin>>b;
    40         cout << KMP() << endl;
    41     }
    42 
    43     return 0;
    44 }

    D - Cyclic Nacklace

    题意就是最少在当前串后面加几个字符使得当前串变成一个循环串。

    思路:当前串记为S串,根据next数组的定义有S[0,next[k]-1] = S[k - next[k],k-1],既前面的部分是完全相等的,那么有S[0,next[len]-1] = S[len-next[len],len-1],所以S串的一个最长循环节就是len-next[len]。

    Why?

    来看下面这个手稿= =

     解决了这个问题之后,就好做了,如果本来就是一个循环串即len%(len-next[len])=0则输出0,否则输出补成循环串需要多少字符。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 
     7 string b;
     8 int nxt[1000005];
     9 
    10 void getnext(){
    11     nxt[0] = -1;
    12     int i = 0, j = -1,len = b.size();
    13     while(i < len){
    14         if(j == -1 || b[i] == b[j])
    15             nxt[++i] = ++j;
    16         else
    17             j = nxt[j];
    18     }
    19 }
    20 
    21 int main(){
    22     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    23     int t;
    24     cin>>t;
    25     while(t--){
    26         cin>>b;
    27         getnext();
    28         int len = b.size();
    29         int cnt = len/(len - nxt[len]);
    30         if(nxt[len] > 0 && len % (len-nxt[len]) == 0)
    31             cout << 0 << endl;
    32         else
    33             cout << (cnt+1)*(len-nxt[len]) - len << endl;
    34     }
    35 
    36     return 0;
    37 }

    E - Period

    题意就是在串的前缀中找循环串,输出位置并输出循环节次数。

    思路同上,每次判断是否循环串即可。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 
     7 string a;
     8 int n;
     9 int nxt[1000005];
    10 
    11 void getnext(){
    12     nxt[0] = -1;
    13     int i = 0, j = -1;
    14     while(i < n){
    15         if(j == -1 || a[i] == a[j])
    16             nxt[++i] = ++j;
    17         else
    18             j = nxt[j];
    19     }
    20 }
    21 
    22 int main(){
    23     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    24     int tot = 1;
    25     while(cin>>n && n){
    26         cin>>a;
    27         getnext();
    28         cout << "Test case #" << tot++ << endl;
    29         for(int i = 1; i <= n; i++){
    30             //cerr << nxt[i] << ' ';
    31             if(nxt[i] > 0 && (i)%(i-nxt[i]) == 0)
    32                 cout << i << " " << i/(i-nxt[i]) << endl;
    33         }
    34         cout << endl;
    35     }
    36 
    37     return 0;
    38 }

    F - The Minimum Length

    原OJ出了点问题,没法做。

    G - Power Strings

    定义A*B就是把B接在串A后面,A^k = A*A^(k-1);现在给你一个串S,问把它写成A^n形式,n最大是多少(A串要你自己找)。

    很简单的一个判断循环串的题,如果串是循环串的话,数组循环次数,否则输出1即可。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 
     7 string a;
     8 int nxt[1000005];
     9 
    10 void getnext(){
    11     nxt[0] = -1;
    12     int i = 0, j = -1,len = a.size();
    13     while(i < len){
    14         if(j == -1 || a[i] == a[j])
    15             nxt[++i] = ++j;
    16         else
    17             j = nxt[j];
    18     }
    19 }
    20 
    21 int main(){
    22     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    23     while(cin>>a && a[0] != '.'){
    24         getnext();
    25         int len = a.size();
    26         if(len%(len-nxt[len]) != 0)
    27             cout << 1 << endl;
    28         else
    29             cout << len / (len - nxt[len]) << endl;
    30     }
    31 
    32     return 0;
    33 }

    H - Seek the Name, Seek the Fame

    题意:在字符串S中,找到字符串A使得A既是S的前缀串,又是S的后缀串,并输出A串第一次出现的位置。

    思路:还是根据next数组的性质,有S[0,next[len]-1] = S[len-next[len],len-1],所以S[0,next[len]-1]是一个满足题意的串,且有S[0,next[next[len]]-1] = S[len-next[next[len]],len-1],所以S[0,next[next[len]]-1]也是一个满足题意的串,所以可以依次往下找直到next数组值为0位置。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <stack>
     6 using namespace std;
     7 
     8 string a;
     9 int nxt[1000005];
    10 
    11 void getnext(){
    12     nxt[0] = -1;
    13     int i = 0, j = -1,len = a.size();
    14     while(i < len){
    15         if(j == -1 || a[i] == a[j])
    16             nxt[++i] = ++j;
    17         else
    18             j = nxt[j];
    19     }
    20 }
    21 
    22 int main(){
    23     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    24     while(cin>>a && a[0] != '.'){
    25         getnext();
    26         stack<int> q;
    27         int len = a.size();
    28         int cnt = 0,j = nxt[len];
    29         while(j > 0){
    30             q.push(j);
    31             j = nxt[j];
    32         }
    33         while(!q.empty()){
    34             cout << q.top() << " ";
    35             q.pop();
    36         }
    37         cout << len << endl;
    38     }
    39 
    40     return 0;
    41 }

    I - Blue Jeans

    找多个串的最长公共子串,且若是字串长度小于3输出"no significant commonalities"。

    开始我还在想这不DP题吗= =,然后发现每组最多10个串,且串长度不超过60,所以直接暴力匹配每个字串。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <stack>
     6 #include <vector>
     7 using namespace std;
     8 
     9 string a,b;
    10 int nxt[1000005];
    11 vector<string> v;
    12 int m;
    13 
    14 class cmp{
    15 public:
    16     bool operator () (const string a, const string b) const {
    17         if(a.size() == b.size())
    18             return a > b;
    19         return a.size() < b.size();
    20     }
    21 };
    22 
    23 void getnext(string a){
    24     nxt[0] = -1;
    25     int i = 0, j = -1,len = a.size();
    26     while(i < len){
    27         if(j == -1 || a[i] == a[j])
    28             nxt[++i] = ++j;
    29         else
    30             j = nxt[j];
    31     }
    32 }
    33 
    34 bool KMP(string b,string s){
    35     int i = 0,j = 0,len1 = b.size(),len2 = s.size();
    36     getnext(s);
    37     while(i < len1){
    38         if(j == -1 || b[i] == s[j])
    39             ++i,++j;
    40         else
    41             j = nxt[j];
    42         if(j == len2)
    43             return 1;
    44     }
    45     return 0;
    46 }
    47 
    48 int main(){
    49     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    50     int t;
    51     cin>>t;
    52     while(t--){
    53         cin>>m>>a>>b;
    54         v.clear();
    55         for(int i = 0,len = a.size(); i < len; i++){
    56             for(int j = 0; i+j < len; j++){
    57                 string s = a.substr(i,j+1);
    58                 if(KMP(b,s))
    59                     v.push_back(s);
    60             }
    61         }
    62         m -= 2;
    63         while(m--){
    64             cin>>b;
    65             for(vector<string>::iterator it = v.begin(); it != v.end();){
    66                 if(!KMP(b,*it))
    67                     it = v.erase(it);
    68                 else
    69                     it++;
    70             }
    71         }
    72         if(v.empty()){
    73             cout << "no significant commonalities" << endl;
    74             continue;
    75         }
    76         string ans = *max_element(v.begin(),v.end(),cmp());
    77         if(ans.size() < 3)
    78             cout << "no significant commonalities" << endl;
    79         else
    80             cout << ans << endl;
    81     }
    82 
    83     return 0;
    84 }

    J - Simpsons’ Hidden Talents

    每组输入两个字符串A和B,找到一个最长的字符串S使得S是A的前缀串且S是B的后缀串,输出串S和S的长度。

    思路:前面说了next数组有S[0,next[len]-1] = S[len-next[len],len-1],同样设主串为S,模式串为T,同样满足S[i-next[j],i-1] = T[0,next[j]-1]这个性质,当i=len时候,有S[len-next[j],len-1] = T[0,next[j]-1],那么T[0,next[j]-1]即是前缀串,S[len-next[j],len-1]为后缀串。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <stack>
     6 #include <vector>
     7 using namespace std;
     8 
     9 string a,b;
    10 int nxt[1000005];
    11 
    12 void getnext(){
    13     int i = 0, j = -1, len = a.size();
    14     nxt[0] = -1;
    15     while(i < len){
    16         if(j == -1 || a[i] == a[j])
    17             nxt[++i] = ++j;
    18         else
    19             j = nxt[j];
    20     }
    21 }
    22 
    23 int KMP(){
    24     getnext();
    25     int i = 0, j = 0,len1 = b.size(), len2 = a.size();;
    26     while(i < len1){
    27         if(j == -1 || b[i] == a[j])
    28             ++i, ++j;
    29         else
    30             j = nxt[j];
    31     }
    32     if(i == len1)
    33         return j;
    34     return 0;
    35 }
    36 
    37 
    38 int main(){
    39     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    40     while(cin>>a>>b){
    41         int ans = KMP();
    42         if(!ans)
    43             cout << 0 << endl;
    44         else
    45             cout << a.substr(0,ans) << ' ' << ans << endl;
    46     }
    47 
    48     return 0;
    49 }

    K - Count the string

    题意:数一个字符串每个前缀串出现的次数并%10007。

    思路:直接去找每个前缀串出现的次数则需要len次KMP,肯定会TLE,所以我们不妨换个思维,我们从next数组下手,根据S[0,next[i]-1] = S[i-next[i],i-1],我们去找第i个位置的next数组,那么S[0,next[i]-1]这个前缀串也就在S[i-next[i],i-1]出现了一次,直到next数组值为0为止。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <stack>
     6 #include <vector>
     7 using namespace std;
     8 
     9 int n;
    10 string a;
    11 int nxt[1000005];
    12 
    13 void getnext(){
    14     int i = 0, j = -1;
    15     nxt[0] = -1;
    16     while(i < n){
    17         if(j == -1 || a[i] == a[j])
    18             nxt[++i] = ++j;
    19         else
    20             j = nxt[j];
    21     }
    22 }
    23 
    24 int main(){
    25     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    26     int t;
    27     cin>>t;
    28     while(t--){
    29         int ans = 0;
    30         cin>>n>>a;
    31         getnext();
    32         for(int i = 1; i <= n; i++){
    33             int tp = nxt[i];
    34             while(tp){
    35                 ans = (ans+1)%10007;
    36                 tp = nxt[tp];
    37             }
    38         }
    39         cout << (ans+n)%10007 << endl;
    40     }
    41 
    42     return 0;
    43 }

    这个题是很好的一个题,不仅考察了next数组的性质,还转变了思维方式,从去找每个串出现次数之和变成了找每个位置包含了哪些前缀串。

    L - Clairewd’s message

    题意:每行输入两个字符串,第一个字符串是一个密码表,a-z对应成密码表上的字符,第二个字符串是密文+明文,密文是完整的但明文部分不全但至少有一个字符是明文,现在问最多可以补全多少个字符并输出。

    思路:如果没有密文表这个东西的话就是个很简单的字符匹配,让密文部分尽量长即可,但现在有了密文所以要稍做修改,我们从(len+1)/2的位置开始KMP,因为密文完整,所以密文至少占用了一半的字符串,当然你也可以拆成两个串进行匹配。

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <map>
     6 using namespace std;
     7 
     8 string a,s;
     9 int nxt[1000005];
    10 
    11 void getnext(){
    12     int i = 0, j = -1,len = a.size();
    13     nxt[0] = -1;
    14     while(i < len){
    15         if(j == -1 || a[i] == a[j])
    16             nxt[++i] = ++j;
    17         else
    18             j = nxt[j];
    19     }
    20 }
    21 
    22 void KMP(){
    23     getnext();
    24     int i = (a.size()+1)/2,j = 0,len = a.size();
    25     while(i < len){
    26         if(j == -1 || s[a[i]-'a'] == a[j])
    27             ++i,++j;
    28         else
    29             j = nxt[j];
    30     }
    31     cout << a;
    32     map<char,char> mp;
    33     for(int i = 0; i < 26; i++)
    34         mp[s[i]] = char('a'+i);
    35     for(int i = j; i < len-j; i++)
    36         cout << mp[a[i]];
    37     cout << endl;
    38 }
    39 
    40 int main(){
    41     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    42     int t;
    43     cin>>t;
    44     while(t--){
    45         cin>>s>>a;
    46         KMP();
    47     }
    48     return 0;
    49 }

    M - Substrings

    输出最长公共子串的长度。不多说直接暴力,稍微优化一下,从长度最短的串开始枚举子串即可。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <map>
     6 using namespace std;
     7 
     8 string s[105];
     9 int nxt[105];
    10 int m;
    11 
    12 void getnext(string a){
    13     int i = 0, j = -1,len = a.size();
    14     nxt[0] = -1;
    15     while(i < len){
    16         if(j == -1 || a[i] == a[j])
    17             nxt[++i] = ++j;
    18         else
    19             j = nxt[j];
    20     }
    21 }
    22 
    23 bool KMP(int k,string b){
    24     getnext(b);
    25     int i = 0,j = 0,len = s[k].size();
    26     while(i < len){
    27         if(j == -1 || s[k][i] == b[j])
    28             ++i,++j;
    29         else
    30             j = nxt[j];
    31         if(j == b.size())
    32             return 1;
    33     }
    34     return 0;
    35 }
    36 
    37 int main(){
    38     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    39     int t;
    40     cin>>t;
    41     while(t--){
    42         string str = "";
    43         int tot = 0;
    44         cin>>m;
    45         for(int i = 0; i < m; i++){
    46             cin>>s[i];
    47             if(str.empty() || s[i].size() < str.size())
    48                 str = s[i];
    49         }
    50         int len = str.size();
    51         for(int i = len; i > 0; i--){
    52             int flag = 1;
    53             for(int j = 0; i+j <= len; j++){
    54                 flag = 1;
    55                 string t1 = str.substr(j,i);
    56                 string t2 = t1;
    57                 //cout << t1 <<endl;
    58                 reverse(t2.begin(),t2.end());
    59                 for(int k = 0; k < m; k++){
    60                     if(!KMP(k,t1) && !KMP(k,t2)){
    61                         flag = 0;
    62                         break;
    63                     }
    64                 }
    65                 if(flag){
    66                     tot = 1;
    67                     cout << t1.size() << endl;
    68                     break;
    69                 }
    70             }
    71             if(flag)
    72                 break;
    73         }
    74         if(!tot)
    75             cout << 0 << endl;
    76     }
    77     return 0;
    78 }

    N - Corporate Identity

    同上,不过这题输出最长公共字串。

    代码:

     1 #include <string>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <map>
     6 using namespace std;
     7 
     8 string s[4005];
     9 int nxt[205];
    10 int m;
    11 
    12 class cmp{
    13 public:
    14     bool operator () (const string a, const string b) const {
    15         if(a.size() == b.size())
    16             return a > b;
    17         return a.size() < b.size();
    18     }
    19 };
    20 
    21 void getnext(string a){
    22     int i = 0, j = -1,len = a.size();
    23     nxt[0] = -1;
    24     while(i < len){
    25         if(j == -1 || a[i] == a[j])
    26             nxt[++i] = ++j;
    27         else
    28             j = nxt[j];
    29     }
    30 }
    31 
    32 bool KMP(int k,string b){
    33     getnext(b);
    34     int i = 0,j = 0,len = s[k].size();
    35     while(i < len){
    36         if(j == -1 || s[k][i] == b[j])
    37             ++i,++j;
    38         else
    39             j = nxt[j];
    40         if(j == b.size())
    41             return 1;
    42     }
    43     return 0;
    44 }
    45 
    46 int main(){
    47     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    48     while(cin>>m && m){
    49         vector<string> v;
    50         string str;
    51         int tot = 0;
    52         for(int i = 0; i < m; i++){
    53             cin>>s[i];
    54             if(str.empty() || s[i].size() < str.size())
    55                 str = s[i];
    56         }
    57         int len = str.size();
    58         for(int i = len; i > 0; i--){
    59             int flag = 1;
    60             v.clear();
    61             for(int j = 0; i+j <= len; j++){
    62                 flag = 1;
    63                 string t1 = str.substr(j,i);
    64                 for(int k = 0; k < m; k++){
    65                     if(!KMP(k,t1)){
    66                         flag = 0;
    67                         break;
    68                     }
    69                 }
    70                 if(flag){
    71                     tot = 1;
    72                     v.push_back(t1);
    73                 }
    74             }
    75             if(flag || !v.empty()){
    76                 cout << *(max_element(v.begin(),v.end(),cmp())) << endl;
    77                 break;
    78             }
    79         }
    80         if(!tot)
    81             cout << "IDENTITY LOST" << endl;
    82     }
    83     return 0;
    84 }

    O - String Problem

    给你一个字符串,每次可以位移一位即把首字母放到尾部,然后问变成字典序最小的串该如何移动且这个串出现了几次,变成字典序最大的串该如何移动且这个串出现了几次。

    最大最小表示法模板,不多说。出现几次判断是否循环串即可。

    代码:

     1 #include <iostream>
     2 #include <string>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <vector>
     6 using namespace std;
     7 
     8 string s;
     9 int nxt[1000005];
    10 
    11 void getnext(string str){
    12     int i = 0, j = -1, len = str.size();
    13     nxt[0] = -1;
    14     while(i < len){
    15         if(j == -1 || str[i] == str[j])
    16             nxt[++i] = ++j;
    17         else
    18             j = nxt[j];
    19     }
    20 }
    21 int posmin(int len){
    22     int i=0,j=1,k=0;
    23     while(i<len&&j<len&&k<len){
    24         int pan=s[(i+k)%len]-s[(j+k)%len];
    25         if(!pan)
    26             k++;
    27         else{
    28             if(pan>0)
    29                 i+=k+1;
    30             else
    31                 j+=k+1;
    32             if(i==j)
    33                 j++;
    34             k=0;
    35         }
    36     }
    37     return min(i+1,j+1);
    38 }
    39 
    40 int posmax(int len){
    41     int i=0,j=1,k=0;
    42     while(i<len&&j<len&&k<len){
    43         int pan=s[(i+k)%len]-s[(j+k)%len];
    44         if(!pan)
    45             k++;
    46         else{
    47             if(pan>0)
    48                 j+=k+1;
    49             else
    50                 i+=k+1;
    51             if(i==j)
    52                 j++;
    53             k=0;
    54         }
    55     }
    56     return min(i+1,j+1);
    57 }
    58 
    59 int main(){
    60     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    61     while(cin>>s){
    62         int cnt = 1;
    63         int len = s.size();
    64         getnext(s);
    65         if(len%(len-nxt[len])==0)
    66             cnt = len/(len-nxt[len]);
    67         cout << posmin(len) << ' ' << cnt << ' ' << posmax(len) << ' ' << cnt << endl;
    68     }
    69 }

    P - How many

    问一组字符串去重后还剩多少,比较方式是只要A能通过位移得到B,就看成相同串,具体题目有解释。

    思路:直接去判断A能否位移得到B很麻烦,换个思路我们把字符串转化成相同样式,然后不久可以直接比较了吗,利用最大最小表示法,我们把输入串转化为最大或最小表示法丢进set即可。

    代码:

     1 #include <iostream>
     2 #include <string>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <set>
     6 using namespace std;
     7 
     8 string s;
     9 int nxt[1000005];
    10 
    11 void getnext(string str){
    12     int i = 0, j = -1, len = str.size();
    13     nxt[0] = -1;
    14     while(i < len){
    15         if(j == -1 || str[i] == str[j])
    16             nxt[++i] = ++j;
    17         else
    18             j = nxt[j];
    19     }
    20 }
    21 string posmin(string s,int len){
    22     int i=0,j=1,k=0;
    23     while(i<len&&j<len&&k<len){
    24         int pan=s[(i+k)%len]-s[(j+k)%len];
    25         if(!pan)
    26             k++;
    27         else{
    28             if(pan>0)
    29                 i+=k+1;
    30             else
    31                 j+=k+1;
    32             if(i==j)
    33                 j++;
    34             k=0;
    35         }
    36     }
    37     int ans = min(i,j);
    38     string res = s.substr(ans,len-ans) + s.substr(0,ans);
    39     return res;
    40 }
    41 
    42 int main(){
    43     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    44     int n;
    45     while(cin>>n){
    46         set<string> v;
    47         for(int i = 0; i < n; i++){
    48             cin>>s;
    49             v.insert(posmin(s,s.size()));
    50         }
    51         cout << v.size() << endl;
    52     }
    53 }

    Q - Period II

    没法做。

    R - Teacher YYF

    神题= =,给你单词的词性,然后给你句子让你判断是否有语法错误,我的思路就是把所有合法情况枚举出来。但暴露了英语不好的事实,WA了,然后发现是合法情况没有找全,然后百度的qwq

    代码:

      1 #include <iostream>
      2 #include <string>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <map>
      6 #include <sstream>
      7 using namespace std;
      8 
      9 map<string,string> mp;
     10 map<string,bool> ste;
     11 map<string,char> str,fun;
     12 
     13 void init(){
     14     fun["n."]='0';
     15     fun["pron."]='1';
     16     fun["adj."]='2';
     17     fun["adv."]='3';
     18     fun["prep."]='4';
     19     fun["art."]='5';
     20     fun["vt."]='6';
     21     fun["vi."]='7';
     22     fun["v."]='8';
     23 
     24     str["450"]='A'; //介词短语
     25     str["4520"]='A';
     26     str["41"]='A';
     27     str["1"]='S'; //主/宾语
     28     str["50"]='S';
     29     str["520"]='S';
     30     str["7"]='I'; //不及物谓语
     31     str["37"]='I';
     32     str["6"]='T'; //及物谓语
     33     str["36"]='T';
     34     str["8"]='V'; //通用谓语
     35     str["38"]='V';
     36     //句子可能的总体结构
     37     ste["SI"]=1;
     38     ste["STS"]=1;
     39     ste["SV"]=1;
     40     ste["SVS"]=1;
     41     ste["ASI"]=1;
     42     ste["ASTS"]=1;
     43     ste["ASV"]=1;
     44     ste["ASVS"]=1;
     45     ste["SAI"]=1;
     46     ste["SATS"]=1;
     47     ste["SAV"]=1;
     48     ste["SAVS"]=1;
     49     ste["SIA"]=1;
     50     ste["STAS"]=1;
     51     ste["SVA"]=1;
     52     ste["SVAS"]=1;
     53     ste["STSA"]=1;
     54     ste["SVSA"]=1;
     55 }
     56 
     57 bool check(string s){
     58     string res = "",c = "";
     59     for(int i = 0; i < s.size(); i++){
     60         c += s[i];
     61         if(str[c] > 0){
     62             res += str[c];
     63             c = "";
     64         }
     65     }
     66     res += c;
     67     if(ste[res])
     68         return true;
     69     return  false;
     70 }
     71 
     72 string work(string s){
     73     stringstream ss(s);
     74     string st,ans;
     75     while(ss>>st){
     76         int len = st.size(),flag = 0;
     77         st[0] = tolower(st[0]);
     78         if(st[len-1] == '.'){
     79             flag = 1;
     80             st.erase(--st.end());
     81         }
     82         if(st[len-1] == ',')
     83             st.erase(--st.end());
     84         ans += fun[mp[st]];
     85         if(flag && !check(ans))
     86             return "NO";
     87     }
     88     return "YES";
     89 }
     90 
     91 int main(){
     92     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
     93     int n,m;
     94     init();
     95     while(cin>>n>>m){
     96         mp.clear();
     97         string s,s1,s2;
     98         for(int i = 0; i < n; i++){
     99             cin>>s1>>s2;
    100             //cout << s1 << " " << s2 <<endl;
    101             mp[s1] = s2;
    102         }
    103         cin.get();
    104         for(int i = 0; i < m; i++){
    105             getline(cin,s);
    106             cout << work(s) << endl;
    107         }
    108     }
    109 }

    S - Best Reward

    给你个价值表,a-z分别就对应相应价值,然后让你找最长回文串并输出价值和。

    马拉车裸题。

    代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <string>
     6 #include <queue>
     7 #include <algorithm>
     8 #include <map>
     9 #include <iomanip>
    10 #include <climits>
    11 using namespace std;
    12 
    13 int n;
    14 int p[1000005],sum[500005],val[27];//sum为前i个字符价值和
    15 int per[500005],pos[500005];//per标记前i个字符为回文串,pos标记后i个字符为回文串
    16 
    17 int manacher(string t){
    18     int len = t.size(),id=0,ans=INT_MIN,temp=0;
    19     for(int i = 1; i <= len; ++i)
    20         sum[i] = sum[i-1] + val[t[i-1]-'a'];
    21     string s = "*#";
    22     for(int i = 0; i < t.size(); ++i) {
    23         s += t[i];
    24         s += "#";
    25     }
    26     for(int i = 2; i < len*2+1; ++i){
    27         if(p[id] + id > i)
    28             p[i]=min(p[2*id-i],p[id]+id-i);
    29         else
    30             p[i] = 1;
    31         while(s[i-p[i]] == s[i+p[i]])
    32             ++p[i];
    33         if(id+p[id] < i+p[i])
    34             id = i;
    35         if(i-p[i] == 0)
    36             per[p[i]-1] = n+1;//表示前缀(前p[i]-1个字符)是回文串
    37         if(i+p[i] == 2*len+2)
    38             pos[p[i]-1] = n+1;//表示后缀(后p[i]-1个字符)是回文串
    39     }
    40     for(int i = 1; i < len; ++i){
    41         if(per[i] == n+1)
    42             temp += sum[i];
    43         if(pos[len-i] == n+1)
    44             temp += sum[len]-sum[i];
    45         if(temp > ans)
    46             ans = temp;
    47         temp=0;
    48     }
    49     return ans;
    50 }
    51 
    52 int main(){
    53     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    54     cin>>n;
    55     while(n--){
    56         string s;
    57         for(int i = 0; i < 26; ++i)
    58             cin >> val[i];
    59         cin>>s;
    60         cout << manacher(s) << endl;
    61     }
    62     return 0;
    63 }

    T - Finding Palindromes

    还没做。

    U - Palindrome

    最长回文串。懒得贴代码了。

    V - 吉哥系列故事――完美队形II

    还没做。

    W - Girls' research

    还没做。

    X - 最长回文

    最长回文串。

    代码:

     1 #include <cstdlib>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <iostream>
     5 #include <algorithm>
     6 using namespace std;
     7 
     8 string t;
     9 int rad[220005];
    10 
    11 void manacher(int len) {
    12     string s = "*#";
    13     for(int i = 0; i < t.size(); ++i) {
    14         s += t[i];
    15         s += "#";
    16     }
    17     for (int i=1,j=0,k; i < len; i+=k) {
    18         while (s[i-j-1] == s[i+j+1]) ++j;
    19         rad[i] = j;
    20         for (k = 1; k <= rad[i] && rad[i-k] != rad[i]-k; ++k) {
    21             rad[i+k] = min(rad[i-k], rad[i]-k);
    22         }
    23         j = max(j-k, 0);
    24     }
    25 }
    26 
    27 int main(){
    28     ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    29     while(cin>>t){
    30         int len = t.size()*2+1;
    31         manacher(len);
    32         cout << *max_element(rad,rad+len) << endl;
    33     }
    34     return 0;
    35 }

    Y - Wow! Such Doge!

    还没做。

    Z - Theme Section

    还没做。


    还有几道题没做,尽快补上。主要还是考察KMP的next数组的性质,然后还有一些前后缀的题也可以用exKMP来做,但暂时偷个懒先水一遍吧。

  • 相关阅读:
    iOS tableHeaderView有默认高度?
    flutter 自定义tabbar 给tabbar添加背景功能
    jar各个版本号的意义
    【转载】springboot + swagger
    分表需要解决的问题 & 基于MyBatis 的轻量分表落地方案
    解决Spring Boot中,通过filter打印post请求的 request body 问题
    SpringBoot自动配置xxxAutoConfiguration 的使用
    Shell
    Spring踩坑记录
    Spring中可复用工具类&&特性记录&&技巧
  • 原文地址:https://www.cnblogs.com/xenny/p/10197527.html
Copyright © 2020-2023  润新知