博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

正睿OI 提高 Day1T3 ZYB玩字符串(DP)

题目链接

设可能的答案串为p,长为len。p一定是s的一个子串且len|n。
虽然一些p在s中可能被断成若干段,但删掉其中的若干段后,这段区间一定会被全部消掉。
于是枚举p后,可以用f[i][j]表示区间[i,j]是否合法。len不需要整除区间长度,多余的部分要匹配p的前缀(匹配什么后缀啊,大不了从前面开始)。
f[i][j]可以由j-1拼,即 \(f[i][j]|=f[i][j-1]\ \&\&\ s[j]==p[(l-1)/l+1] (l=j-i+1)\)
也可以是j和前面构成了可消除的串,之后可以跳过这些串继续匹配,即 \(f[i][j]|=f[i][j-k*len]\ \&\&\ f[j-k*len+1][j]\)
第二种转移的上界是\(O(\frac{n}{len})\)的。
复杂度约是\(O(\sum_{len|n}(n-len+1)*n^2*n/len)=O(n^4)\)。对p判一下重,再判字符集合法性,跑的就很快了。

//285ms	17216kb(string怎么那么快。。)
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mod (20000000)
const int N=205;

int n,q[N*N],cnt[233],cnt2[233];
bool vis[20000001],f[N][N];
char s[N],p[N],ans[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline int Hash(int l)
{
	int x=0;
	for(int i=1; i<=l; ++i) x=(x*37+p[i]-'A')%mod;
	return x;
}
void Update()
{
	if(ans[1]=='{') for(int i=1; i<=n; ++i) ans[i]=p[i];
	else
	{
		for(int i=1; i<=n; ++i) if(p[i]>ans[i]) return;
		for(int i=1; i<=n; ++i) ans[i]=p[i];
	}
}
bool Solve(int len)
{
	for(int i=1; i<=n; ++i) f[i][i]=s[i]==p[1];
	for(int l=2; l<=n; ++l)
	{
		for(int i=1,j; (j=i+l-1)<=n; ++i)
		{
			f[i][j]=0;
			if(f[i][j-1]&&s[j]==p[(l-1)%len+1]) f[i][j]=1;
			else
				for(int k=1; j>=i+k*len; ++k)
					if(f[i][j-k*len]&&f[j-k*len+1][j]) {f[i][j]=1; break;}
		}
	}
	return f[1][n];
}

int main()
{
	for(int T=read(); T--; )
	{
		ans[1]='{'; int t=0;
		scanf("%s",s+1), n=strlen(s+1);// cin>>s; s=""+s;
		for(int i=1; i<=n; ++i) ++cnt[s[i]];
		for(int len=1; len<=n; ++len)
			if(!(n%len))
			{
				for(int l=0,val; l+len<=n; ++l)
				{
					for(int i=1; i<=len; ++i) p[i]=s[l+i];// p=s.substr(l+1,l+len)

					for(int i=1; i<=len; ++i) ++cnt2[p[i]];
					bool f=1;
					for(int i='a'; i<='z'; ++i) if(cnt2[i]&&cnt[i]%cnt2[i]) {f=0; break;}//cnt2!=0。。
					for(int i=1; i<=len; ++i) --cnt2[p[i]];
					if(!f) continue;

					if(!vis[val=Hash(len)]) vis[val]=1, q[++t]=val;
					else continue;

					if(Solve(len)) Update();
				}
				if(ans[1]!='{') {ans[len+1]='\0', puts(ans+1); break;}
			}
		for(int i=1; i<=t; ++i) vis[q[i]]=0;
		for(int i=1; i<=n; ++i) --cnt[s[i]];
	}
	return 0;
}
posted @ 2018-08-27 16:41  SovietPower  阅读(410)  评论(0编辑  收藏  举报