Fork me on GitHub

[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来做,但暂时偷个懒先水一遍吧。

posted @ 2018-12-29 21:53 Xenny 阅读(...) 评论(...) 编辑 收藏