题目链接:
Codeforces #349 Div2 C Reberland Linguistics
http://codeforces.com/contest/667/problem/C
题意:
给一个只含小写字母的字符串,从第一个字符至少取四个作为根,后面会跟若干个只含2个或3个字母的子串,
“The only restriction — it is not allowed to append the same string twice in a row”
这句话巨坑,一开始以为是一行不能含有两个相同的子串,谁知in a row居然还有连续的意思,所以真实的题意是不能有两个连续相同的子串。把后面的所有2个或3个字母的子串称为后缀串。
问有多少个后缀串,并按字典序输出这些后缀串。
样例:
abacabaca
3
aca
ba
ca
abaca
0
分析:
我是只考虑从后往前和最后一个字母相同的最大连续子串的长度same。当出现不同字母时,就可以认为从这个位置开始取2个或者3个字母均可以。先将字符串翻转,从下标0开始一直扫描到len-7,(len是字符串的长度),针对same的值考虑的边界情况比较多。
给几组样例对照着代码看可能好点。
abcdeabzzzzzzzz
5
ab
abz
bz
zz
zzz
bbbbbccaaaaaa
4
aa
aaa
ca
cca
dddddaabbbbbb
4
aab
ab
bb
bbb
aaaaababaaaaaaaaaaaa
6
aa
aaa
ab
ba
baa
bab
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
using namespace std;
const int MAX_N=10100;char s[MAX_N];
int vis1[30][30],vis2[30][30][30];struct Ans{char str[10];bool operator < (const struct Ans& rhs) const {return strcmp(str,rhs.str) < 0;}
}ans[MAX_N*100];int main()
{freopen("C.in","r",stdin);while(~scanf("%s",s)){int len=strlen(s);if(len<=6) {printf("0\n");continue;}memset(vis1,0,sizeof(vis1));memset(vis2,0,sizeof(vis2));for(int i=0;i<len/2;i++){swap(s[i],s[len-1-i]);}//puts(s);int a,b,c,same=-1,flag=0;for(int i=0;i<=len-7;i++){if(flag) same=-1;if(s[i]!=s[0]) flag=1;same++;if(i==1) continue;if(same>5&&(same%5!=0&&(same-2)%5!=0&&(same-3)%5!=0) )continue;if(i==4&&s[0]==s[2]&&s[1]==s[3]) continue;a=s[i+2]-'a',b=s[i+1]-'a',c=s[i]-'a';vis1[b][c]=1;if(i<len-7) vis2[a][b][c]=1;}int total=0;for(int i=0;i<30;i++){for(int j=0;j<30;j++){if(vis1[i][j]){char tmp[5];tmp[0]=i+'a',tmp[1]=j+'a',tmp[2]='\0';strcpy(ans[total++].str,tmp);}}}for(int i=0;i<30;i++){for(int j=0;j<30;j++){for(int k=0;k<30;k++){if(vis2[i][j][k]){char tmp[5];tmp[0]=i+'a',tmp[1]=j+'a',tmp[2]=k+'a',tmp[3]='\0';strcpy(ans[total++].str,tmp);}}}}sort(ans,ans+total);printf("%d\n",total);for(int i=0;i<total;i++){puts(ans[i].str);}}return 0;
}
看了别人家的代码,发现这种处理可能更科学、容易理解些、、、
从倒数第二个位置开始向前扫描到第6个位置,在第i个位置判断s[i]-s[i+1]以及s[i]-s[i+1]-s[i+2]是否合法。
对每个位置设置两个布尔类型变量,two_valid[i]和three_valid[i]表示从i开始向后2/3个字母是否可取。初始均为false。
用distance表示从i位置到字符串末尾的距离。
当distance=2时,两个字母一定合法。
当distance=3时,三个字母一定合法。
当distance=4时,
只有在i+2位置两个字母合法并且从i开始的两个字母和从i+2开始的两个字母不同时,在i位置的两个字母情况才合法。
当distance=5时,两个字母和三个字母情况均合法。
当distance>5时,
考虑两个字母合法的情况。
当从i开始的两个字母和从i+2开始的两个字母相同时,只有three_valid[i+2]=true时,才能在i位置取两个字母并且two_valid[i]=true;
当从i开始的两个字母和从i+2开始的两个字母不同时,只要three_valid[i+2]和two_valid[i+2]任一为true即可。
考虑三个字母合法的情况。
当从i开始的三个字母和从i+3开始的三个字母相同时,只有two_valid[i+3]=true时,才能在i位置取三个字母,并且three_valid[i]=true;
当从i开始的三个字母和从i+3开始的三个字母不同时,只要three_valid[i+3]和two_valid[i+3]任一为true即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#include <vector>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;int main()
{IOS;freopen("C.in","r",stdin);string s;while(cin>>s){int len = (int)(s.size());if(len <= 6){printf("0\n");continue;}vector<bool> two_valid(s.size(),false);vector<bool> three_valid(s.size(),false);vector<string> ans;for(int i=len-2;i>=5;i--){int distance=len-i;if(distance==2){two_valid[i]=true;ans.push_back(s.substr(i,2));}else if(distance == 3){three_valid[i] = true;ans.push_back(s.substr(i,3));}else if(distance == 4) {if(s.substr(i,2) != s.substr(i+2,2)){two_valid[i] =true;ans.push_back(s.substr(i,2));}}else if(distance == 5) {two_valid[i] =true;three_valid[i] =true;ans.push_back(s.substr(i,2));ans.push_back(s.substr(i,3));}else {if(s.substr(i,2)==s.substr(i+2,2)){if(three_valid[i+2]) {two_valid[i]=true;ans.push_back(s.substr(i,2)); //依然需要push_back().因为不一定ans里已经有了s.substr(i+2,2)}}else if(two_valid[i+2] || three_valid[i+2] ) {two_valid[i]=true;ans.push_back(s.substr(i,2));}if(s.substr(i,3)==s.substr(i+3,3)){if(two_valid[i+3]){three_valid[i]=true;ans.push_back(s.substr(i,3));}}else if(two_valid[i+3] || three_valid[i+3] ) {three_valid[i]=true;ans.push_back(s.substr(i,3));}}}sort(ans.begin(),ans.end());int total = unique(ans.begin(),ans.end())-ans.begin();cout << total <<endl;for(int i = 0 ;i < total ;i++){cout << ans[i] << endl;}}return 0;
}