ZROI2018提高day1t3

传送门

分析

考场上想到了先枚举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;
}
posted @ 2018-09-01 13:31  水题收割者  阅读(133)  评论(0编辑  收藏  举报