题解 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;
}