[JZOJ6347]:ZYB玩字符串(DP+记忆化搜索)

题目描述

  $ZYB$获得了一个神秘的非空字符串$p$。
  初始时,串$S$是空的。
  $ZYB$会执行若干次这样的操作:
  $1.$选取$S$中的一个任意的位置(可以是最前面或者最后面)
  $2.$在这个位置上插入一个完整的$p$,得到一个新的$S$。
  但是$ZYB$不小心把$p$弄丢了。
  他告诉你现在的$S$是什么,请帮他还原出可能的$p$。
  如果有多个$p$符合要求,选取长度最短的。
  如果仍然有多解,选取字典序最小的。


输入格式

  从文件$string.in$中读入数据。
  这道题有多组数据,第一行一个数$T$,表示数据组数。
  对于每组数据,读入一行字符串,表示$S$。


输出格式

  输出到文件$string.out$中。
  一共$T$行,每行一个字符串$p$,表示对应的答案。


样例

样例输入:

1
hhehellolloelhellolo

样例输出:

hello


数据范围与提示

样例解释:

  $S$为:
  $1.$
  $2.hello$
  $3.hhelloello$
  $4.hhelloelhellolo$
  $5.hhehellolloelhellolo$

数据范围:

  前$20\%$:$|S|\leqslant 8$
  前$40\%$:$|S|\leqslant 20$
  前$60\%$:$|S|\leqslant 100,\sum|S|\leqslant 300$
  另有$10\%$:$S$是$p$等概率插入可行位置构造出来的。
  另有$10\%$:$p$的长度不超过$3$。
  $100\%$:$|S|\leqslant 200,T\leqslant 10,\sum|S|\leqslant 666$


题解

因为串$S$肯定有一段连续的是$p$(最后插进去的),所以可以枚举这个连续的串,再想办法判断就好了。

考虑$DP$,定义怪怪的,设$dp[l][r]$表示在当前枚举的长度为$len$的情况下,区间$[l,r]$除了前$(r-l)%len$位匹配了整串前缀以外剩下的部分都匹配了是否可行。

那么考虑转移,设$b$数组为当前$check$的这段区间:

  $\alpha.dp[l][r]|=dp[l][r-1]\&(s[j]==b[(j-i)%len+1])$:多匹配了一位。

  $\beta.dp[l][r]|=dp[i][j-k\times len]\&f[j-k\times len+1][j]$:后面有几段匹配了。

由于$\beta$转移的上界为$\frac{N}{len}$,所以最终的时间复杂度为$\Theta(\sum\limits_{len|N}(N-len+1)\times N^2\times\frac{N}{len}=\Theta(N^4)$。

不过可以对于每一个$p$,先判断一下其子集合法性,然后使用记忆化搜索即可。

时间复杂度:$\Theta(N^4)$(但是远远达不到)。

期望得分:$100$分。

实际的分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
char ch[201];
int n,a[201],b[201],ans[201];
int dp[201][201];
int mod(int x,int y){return x>y?mod(x-y,y):x;}
bool dfs(int x,int l,int r)
{
	if(l>r)return 1;
	if(dp[l][r]!=-1)return dp[l][r];
	for(int mid=r-x;l<=mid;mid-=x)
		if(dfs(x,l,mid)&&dfs(x,mid+1,r))
			return dp[l][r]=1;
	if(a[r]==b[mod(r-l+1,x)])return dp[l][r]=dfs(x,l,r-1);
	return dp[l][r]=0;
}
bool judge(int x){for(int i=1;i<=x;i++)if(ans[i]>b[i])return 1;return 0;}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		scanf("%s",ch+1);
		n=strlen(ch+1);
		for(int i=1;i<=n;i++)a[i]=ch[i]-'a'+1;
		for(int i=1;i<=n;i++)
		{
			if(n%i)continue;bool res=0;
			memset(ans,0,sizeof(ans));
			for(int j=1;j<=n-i+1;j++)
			{
				memset(dp,-1,sizeof(dp));
				for(int k=1;k<=i;k++)b[k]=a[j+k-1];
				if(dfs(i,1,n)&&(!ans[1]||judge(i)))
					for(int k=1;k<=i;k++)ans[k]=b[k];
			}
			if(ans[1]){for(int k=1;k<=i;k++)printf("%c",(char)(ans[k]+'a'-1));puts("");break;}
		}
	}
	return 0;
}

rp++

posted @ 2019-11-13 16:28  HEOI-动动  阅读(371)  评论(0编辑  收藏  举报