题解 ZYB玩字符串

传送门

基本完全不会

首先发现 \(p\) 一定是 \(s\) 的子串,可以枚举 \(p\) 来check
\(len_p \mid n\)\(p\) 中包含 \(s\) 中所有字母且成比例,可以剪枝
然后考虑优化check过程
贪心每次取第一次完整出现去删是假的,考虑 \(s=ababaa,\ p=aba\)
有个神仙DP(其实这个思路也可以从爆搜引入)
\(f[i, j]\) 表示区间 \([i, j]\) 是否合法
这里对「合法」的定义是:
若区间长为 \(len\) 的倍数,表示这个区间能否被 \(p\) 构成
若不是倍数,表示这个区间在删去一些插进来的 \(p\) 之后剩下的不足 \(len\) 的部分是不是 \(p\) 的前缀
这个DP很巧妙地使得它可以转移了
转移考虑加入一个字符
\(f_{i, j} |= f_{i, j-1}\&(s_j=prefix_{(j-x)\%len+1})\)
\(f_{i, j} |= f_{i, j-k*len}\&f_{j-k*len+1, j}\)
第二个转移即为考虑 \(j\) 属于一个整块,去枚举整块的边界
具体可以记搜实现,可以卡掉一些状态
复杂度大约 \(O(n^4)\)

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 210
#define ll long long
#define reg register int
//#define int long long 

int n;
int cnt[26], len, cnt2[26];
char s[N], pre[N], bkp[N];
short dp[N][N];

inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}

bool dfs(int i, int j) {
	if (i>j) return 1;
	if (~dp[i][j]) return dp[i][j];
	bool t=dfs(i, j-1)&&(s[j]==pre[(j-i)%len+1]);
	for (int k=1; (k*len<j-i+1)&&!t; ++k)
		t|=dfs(i, j-k*len)&&dfs(j-k*len+1, j);
	dp[i][j]=t;
	return t;
}

void solve() {
	memset(cnt, 0, sizeof(cnt));
	for (int i=1; i<=n; ++i) ++cnt[s[i]-'a'];
	int g=0;
	for (int i=0; i<26; ++i) if (cnt[i]) {
		if (g) g=gcd(g, cnt[i]);
		else g=cnt[i];
	}
	for (len=1; len<=n; ++len) if (!(n%len)) {
		bool already=0;
		for (int i=len; i<=n; ++i) {
			for (int j=i-len+1; j<=i; ++j)
				pre[j-i+len]=s[j];
			if (already) {
				for (int j=1; j<=len; ++j)
					if (pre[j]>bkp[j]) goto jump;
					else if (pre[j]<bkp[j]) break;
			}
			memset(cnt2, 0, sizeof(cnt2));
			for (int j=1; j<=len; ++j) ++cnt2[pre[j]-'a'];
			for (int j=0; j<26; ++j) {
				//if (cnt[i]) assert(cnt[i]||cnt2[i]), assert((cnt[i]&&cnt2[i])), assert(!((cnt[i]||cnt2[i])^(cnt[i]&&cnt2[i])));
				//cout<<cnt[j]<<' '<<cnt2[j]<<endl;
				//cout<<(cnt[j]||cnt2[j])<<' '<<(cnt[j]&&cnt2[j])<<' '<<((cnt[j]||cnt2[j])^(cnt[j]&&cnt2[j]))<<endl;
				if ((cnt[j]||cnt2[j])^(cnt[j]&&cnt2[j])) goto jump;
				if (cnt2[i] && g%cnt2[i]) goto jump;
			}
			memset(dp, -1, sizeof(dp));
			if (dfs(1, n)) {
				already=1;
				for (int j=1; j<=len; ++j) bkp[j]=pre[j];
			}
			jump: ;
		}
		if (already) {
			bkp[len+1]='\0';
			printf("%s\n", bkp+1);
			return ;
		}
	}
}

signed main()
{
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%s", s+1);
		n=strlen(s+1);
		solve();
	}
	
	return 0;
}
posted @ 2021-09-14 21:12  Administrator-09  阅读(5)  评论(0编辑  收藏  举报