分析
考场上想到了先枚举p的长度,在枚举这个长度的所有子串,期望得分40~50pts,但是最终只得了20pts,这是因为我写的代码在验证中总是不断删除s'中的第一个p,而这种方式不能解决形如ababaa的字符串。于是我们考虑满分做法。设dp[i][j]表示在当前的p的情况下s的[i,j]位置是否满足。满足是指这一段区间只由p构成或者一部分只由p构成,另一部分则是p的前缀。对于位置j的字符可能有两种情况:
1.和之后数个字符拼成一段,转移:
dp[i][j]|=(dp[i][j-1]&(s[j]==p[(j-i)%len+1]))。
2.和之前的一段字符拼在一起,中间已经有k个p,转移:
dp[i][j]|=(dp[i][j-k*len]&dp[j-k*len+1][j])。
然后我们使用记忆化搜索转移即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int inf = 1e9+7;
int dp[300][300],n,m,cnt,len;
char s[300],t[300],ans[300];
inline int dfs(int le,int ri){
if(le>ri)return 1;
if(dp[le][ri]!=-1)return dp[le][ri];
dp[le][ri]=0;
if(dfs(le,ri-1)&&s[ri]==t[(ri-le)%len+1])dp[le][ri]|=1;
for(int i=ri-len;i>=le;i-=len)
if(dfs(le,i)&&dfs(i+1,ri))dp[le][ri]|=1;
return dp[le][ri];
}
inline void getmin(){
if(cnt>len){
for(int i=1;i<=len;i++)ans[i]=t[i];
cnt=len;
return;
}
for(int i=1;i<=cnt;i++)
if((ans[i]-'0')<(t[i]-'0'))return;
else if((ans[i]-'0')>(t[i]-'0'))break;
for(int i=1;i<=cnt;i++)ans[i]=t[i];
}
int main(){
int i,j,k,T;
scanf("%d",&T);
while(T--){
cnt=inf;
scanf("%s",t);
n=strlen(t);
for(i=1;i<=n;i++)s[i]=t[i-1];
for(i=1;i<=min(n,cnt);i++)
if(!(n%i))for(j=1;j+i-1<=n;j++){
memset(dp,-1,sizeof(dp));m=i;
for(k=1;k<=i;k++)t[k]=s[j+k-1];
len=i;if(dfs(1,n))getmin();
}
for(i=1;i<=cnt;i++)cout<<ans[i];
puts("");
}
return 0;
}