题目

题目大意

有一个字符串\(p\)。一开始字符串\(s\)为空串。

接下来进行若干次操作:在\(s\)的某个空隙中插入\(p\)。

给出操作后的\(s\),问长度最小的\(p\)。


思考历程

感觉是一道神仙题。

于是考虑暴力。

在\(s\)前面找连续的最长串,作为\(p\)的前缀。显然这个串中只出现过一次\(s_1\)

同样地,在后面也找一条,作为后缀。

将前缀出现的位置和后缀出现的位置标记一下。

统计每个字符出现的个数,求最大公因数\(g\),表明操作的次数为\(g\)的因数。

然后按照长度从小到大枚举子串,如果当前子串的头和尾都被标记了,并且中间的字符个数的比例和整个字符串的比例相等,就取这个字符串作为\(p\)暴力判断。

暴力判断的时候,每次用\(KMP\)找出一个子串,将它删去,递归。

如果有多个这样的子串,那就分别搞。

然而出现了某细节错误,导致只有\(10\)分。


正解

这题的正解居然是\(DP\)!

考虑一个字符串,它的所有元素会在后面的操作中逐渐分离,但是它们的相对顺序是不变的。

而且它原来相邻的两个元素在后面的操作之后,两个元素之间的空间可以转化成个子问题。如果可行,则这个子问题也必定可行。

先考虑普通的\(DP\)思路:\(f_{l,r,k}\)表示区间\([l,r]\)中,有零散的\(k\)个字母拼起来等于\(p_{1..k}\),其它的都合法,是否可行。

转移有两种:一种是从\(f_{l,r-1,k-1}\)转移过来,条件是\(s[j]=p[k]\),就是加上一个零散的元素。

另一种是从\(f_{l,j,k} \ and \ f_{j,r,0}\),就是在后面拼一个合法的。

然而这样会\(TLE\)。

紧接着我们发现,实际上,\(k=(r-l+1)\mod len\)

所以就可以省去一维,然后就可以\(AC\)了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 210
inline int gcd(int a,int b){
while (b){
int k=a%b;
a=b;
b=k;
}
return a;
}
int n,nt;
char s[N],t[N],s2[N][N];
int buc[255],b2[255];
int m;
char c[255];
bool beg[N],end[N];
int p[N];
bool f[N][N];
inline bool work(int k,int len){
nt=len;
for (int i=1;i<=nt;++i)
t[i]=s[k+i-1];
memset(f,0,sizeof f);
for (int i=n;i>=1;--i){
f[i][i]=(s[i]==t[1]);
for (int j=i+1;j<=n;++j){
f[i][j]|=(f[i][j-1]&&s[j]==t[(j-1-i+1)%nt+1]);
for (int k=i+(j-i)%nt;k<j && !f[i][j];k+=nt)
f[i][j]|=(f[i][k]&&f[k+1][j]);
}
}
return f[1][n];
}
int 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);
memset(buc,0,sizeof buc);
for (int i=1;i<=n;++i)
buc[s[i]]++;
m=0;
for (char ch=' '+1;ch<='~';++ch)
if (buc[ch])
c[++m]=ch;
int g=n;
for (int i=1;i<=m;++i)
g=gcd(g,buc[c[i]]);
int l=1,r=n;
while (l<n && s[l+1]!=s[1])
++l;
while (r>1 && s[r-1]!=s[n])
--r;
memset(beg,0,sizeof beg);
memset(end,0,sizeof end);
for (int i=1,j;i+l-1<=n;++i){
for (j=1;j<=l;++j)
if (s[i+j-1]!=s[j])
break;
if (j<=l)
continue;
beg[i]=1;
}
for (int i=n,j;i+r-n>=1;--i){
for (j=n;j>=r;--j)
if (s[i+j-n]!=s[j])
break;
if (j>=r)
continue;
end[i]=1;
}
for (int len=max(l,n-r+1);len<=n;++len)
if (n%len==0 && g%(n/len)==0){
int t=n/len,hg=0;
memset(b2,0,sizeof b2);
for (int i=1;i<=len;++i)
b2[s[i]]++;
for (int i=1;i<=m;++i)
if (b2[c[i]]*t==buc[c[i]])
hg++;
int i;
for (i=1;i+len-1<=n;++i){
if (beg[i] && end[i+len-1] && hg==m){
if (work(i,len)){
for (int j=i;j<i+len;++j)
putchar(s[j]);
putchar('\n');
break;
}
}
if (b2[s[i]]*t==buc[s[i]])
hg--;
b2[s[i]]--;
if (b2[s[i]]*t==buc[s[i]])
hg++;
if (b2[s[i+len]]*t==buc[s[i+len]])
hg--;
b2[s[i+len]]++;
if (b2[s[i+len]]*t==buc[s[i+len]])
hg++;
}
if (i+len-1<=n)
break;
}
}
return 0;
}

总结

这都想不出来……我真是太菜了……

[JZOJ6347] 【NOIP2019模拟2019.9.8】ZYB玩字符串的更多相关文章

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

    题目描述 $ZYB$获得了一个神秘的非空字符串$p$. 初始时,串$S$是空的. $ZYB$会执行若干次这样的操作: $1.$选取$S$中的一个任意的位置(可以是最前面或者最后面) $2.$在这个位置 ...

  2. 6424. 【NOIP2019模拟2019.11.13】我的订书机之恋

    题目描述 Description Input Output Sample Input 见下载 Sample Output 见下载 Data Constraint 题解 lj题卡线段树 求出每个右端点往 ...

  3. 6392. 【NOIP2019模拟2019.10.26】僵尸

    题目描述 题解 吼题但题解怎么这么迷 考虑一种和题解不同的做法(理解) 先把僵尸离散化,h相同的钦(ying)点一个大小 (可以发现这样每种情况只会被算正好一次) 计算完全被占领的方案,然后1-方案/ ...

  4. 6389. 【NOIP2019模拟2019.10.26】小w学图论

    题目描述 题解 之前做过一次 假设图建好了,设g[i]表示i->j(i<j)的个数 那么ans=∏(n-g[i]),因为连出去的必定会构成一个完全图,颜色互不相同 从n~1染色,点i的方案 ...

  5. 6377. 【NOIP2019模拟2019.10.05】幽曲[埋骨于弘川]

    题目描述 题解 随便bb 详细题解见 https://www.cnblogs.com/coldchair/p/11624979.html https://blog.csdn.net/alan_cty/ ...

  6. 6364. 【NOIP2019模拟2019.9.20】养马

    题目描述 题解 一种显然的水法:max(0,-(点权-边权之和*2)) 这样会挂是因为在中途体力值可能会更小,所以考虑求走完每棵子树所需的至少体力值 考虑从子树往上推求出当前点的答案 设每棵子树从根往 ...

  7. 6362. 【NOIP2019模拟2019.9.18】数星星

    题目描述 题解 一种好想/好写/跑得比**记者还快的做法: 对所有询问排序,按照R递增的顺序来处理 维护每个点最后一次被覆盖的时间,显然当前右端点为R时的答案为所有时间≥L的点的权值之和 LCT随便覆 ...

  8. 6359. 【NOIP2019模拟2019.9.15】小ω的树(tree)(定期重构)

    题目描述 题解 qy的毒瘤题 CSP搞这种码农题当场手撕出题人 先按照边权从大到小建重构树,然后40%暴力修改+查找即可 100%可以定期重构+平衡规划,每次把B个询问拉出来建虚树,在虚树上暴力维护每 ...

  9. 【NOIP2019模拟2019.11.13】旅行 && GDKOI2018 还念(二分答案+dij)

    Description: 题解: 显然满足二分性. 并且每一条边要不选l要不选r. 二分的那条链肯定要选l. 考虑有两个人在走最短路,一个人一开始必须走二分的那条链,要求第一个人走的比第二个人快. 安 ...

随机推荐

  1. mysql(自动添加系统时间)timestamp类型字段的CURRENT_TIMESTAMP与ON UPDATE CURRENT_TIMESTAMP属性

    timestamp有两个属性,分别是CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP两种,使用情况分别如下: 1.CURRENT_TIMESTAMP 当要向 ...

  2. loop find column

    declare l_sql varchar2(500); -- variable that contains a query l_c sys_refcursor; -- cursor variable ...

  3. Codeforces 1151E 统计贡献

    题意:给你一个数组a,设函数f(l, r)为数组a中权值在[l, r]之间的连通块的数目,比如a = [1, 3, 2, 1], f(1, 2) = 2, 连通块是位置1和位置3,4.问Σ(i = 1 ...

  4. js字符与ASCII码互转的方法

    大写字母A-Z对应的ASCII码值是65-90 小写字母a-z对应的ASCII码值是97-122 将字母转为ascii码的方法: 将ascii码转为对应字母的方法:

  5. IconFont 图标的3种引用方式

    第一步:进入阿里巴巴矢量图网站:http://www.iconfont.cn/   阿里巴巴矢量图 第二步:搜索你分类的关键字---然后加入购物车,下载到本地,然后解压,会将合并后的字体文件及自动生成 ...

  6. 强大的httpClientUtils

    <!-- https://mvnrepository.com/artifact/com.arronlong/httpclientutil --> <dependency> &l ...

  7. 每天进步一点点-深度学习入门-基于Python的理论与实现 (2)

    今天要补上两天的 不补了,新手,看的比较慢-- 手写识别例子跳过先 思考如何实现数字5的识别 三种方法: 训练数据:学习,寻找最优解 测试数据:评价模型能力. 损失函数:以损失函数为线索寻找自由权重参 ...

  8. 【NOI2019模拟2019.7.4】朝夕相处 (动态规划+BM)

    Description: 题解: 这种东西肯定是burnside引理: \(\sum置换后不动点数 \over |置换数|\) 一般来说,是枚举置换\(i\),则\(对所有x,满足a[x+i]=a[i ...

  9. luoguP1134 阶乘问题 [数论]

    题目描述 也许你早就知道阶乘的含义,N阶乘是由1到N相乘而产生,如: 12! = 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x 11 x 12 = 479,001, ...

  10. 后缀自动机模板——不同子串个数p2408

    后缀自动机的入门博客 https://www.luogu.org/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie 有两种求法,分别对应了两种性质 #includ ...