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