题目

题目大意

有一个字符串\(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. C#操作Word的+ CKEditor 輸出成Word文件(包含圖案上傳)

    C#操作Word 参考博文: C#操作word类文件 https://www.cnblogs.com/walking/p/3571068.html C#中的Office操作专栏(21) http:// ...

  2. pandas--层次化索引

    层次化索引是pandas的一项重要功能,它使你能在一个轴上拥有多个(两个以上)索引级别. 创建一个Series,并用一个由列表或数组组成的列表作为索引. data=Series(np.random.r ...

  3. qt创建无qt工程

    qt创建无qt工程,cmake . eclipse 编写makefile  代码

  4. PHP面向对象----- 类的自动加载

    1.类的自动加载 spl_autoload_register函数 test.php <?php spl_autoload_register('autoload'); // require_onc ...

  5. Batch - FOR %%a %%b

    总结 %%a refers to the name of the variable your for loop will write to. Quoted from for /?: FOR %vari ...

  6. Quick BI功能篇之(一):20分钟入门

    前言: 最近小编帮助隔壁团队一个小姐姐解决了个大难题:给老板汇报业绩分析,频次提高.效率提升,还得保证团队中的小伙伴们都得有点大数据时代的基本数据能力.小编觉得这么好的经验可以分享给更多志同道合的朋友 ...

  7. Delphi GDI(一)

    Delphi 7下IGDIPlus库的使用 IGDI+是一个免费开源封装微软GDI+功能的Delphi库,该库使得可以用Delphi语言代码快速简短的实现复杂GDI+应用程序. 官方网站:http:/ ...

  8. kafaka集群部署

    1.集群规划 kafka集群配置是依赖zookeeper的,所以需要保证先安装了zookeeper和jdk注意:kafka内自带zookeeper,我们不使用自带的. hadoop101 hadoop ...

  9. NX二次开发-UFUN求两个向量的叉乘UF_VEC3_cross

    NX9+VS2012 #include <uf.h> #include <uf_ui.h> #include <uf_vec.h> #include <uf_ ...

  10. MongoDB点滴

    0 http://blog.csdn.net/mydeman/article/details/6652387 1 MongoDB 内置连接池,不需要使用额外的连接池驱动 Note: The Mongo ...