1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:22:48,当前版本为作者最后更新于2020-03-17 14:19:18,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意简述:给定两个字符串 。每次可以切除 的一个前/后缀并保证其为切割后的 的子串。求得到 的最少操作数或给出无解。
题目可以理解为:每次操作将 的一个子串拼在其前/后。求得到 的最少操作数。
一些约定:
设 。设区间 为 的第 位至第 位字符组成的字符串。
- 如何判断无解:如果 没有在 中出现,或者 出现了 没有的字母,即无解。可以 暴力判断,也可以 KMP 求解。std 使用 暴力判断。
Subtask :。
输出 即可。
Subtask : 仅包含小写字母 。
先判断是否无解,否则答案为最小的满足 成立的整数 。
Subtask :。
区间 DP。设 为 扩展到区间 的最少步数。转移时枚举粘贴字符串的长度,暴力匹配。枚举 ,转移枚举长度 ,暴力匹配 。时间复杂度 ,常数极小。
Subtask :。
将暴力匹配改成字符串哈希即可做到 匹配。总时间复杂度 ,常数较小。
如果实现精细 + 极小常数, 也许可以暴力艹过去。
Subtask :。
验题人
https://www.luogu.com.cn/user/138400法时间复杂度为 。这里放上他的原话(有删减):“如果已经算出了 ,考虑它能影响哪些更大的区间的 值。显然只能从两端扩展。”
“以往左为例,能扩展到 (能影响 )当且仅当 是 的子串。那么所有 显然构成区间。”
“用 算法算出 中两两点的 LCS,区间 RMQ 即可得到最小的 ,然后线段树区间更新维护。”
“还有一个小小的优化方法可以把它弄成 BIT。”
这里给出他的代码:
#include<bits/stdc++.h> using namespace std; const int inf=0x3f3f3f3f; int lowbit(int x){return x&-x;} const int N=1500,M=1500; int n,m; char a[N+5],b[M+5]; int cov[N+1][M+1]; int lcP[N+1][M+1],Lcs[M+1][M+1]; int s; char c[N+M+6]; void con(char s1[],int p1,int l1,char s2[],int p2,int l2){ s=0; for(int i=p1;i<=l1;i++)c[++s]=s1[i]; c[++s]='!'; for(int i=p2;i<=l2;i++)c[++s]=s2[i]; } int z[N+M+2]; void z_init(){ z[1]=s; int zl=0,zr=0; for(int i=2;i<=s;i++) if(zr<i){ z[i]=0; while(i+z[i]<=s&&c[i+z[i]]==c[1+z[i]])z[i]++; if(z[i])zl=i,zr=i+z[i]-1; } else if(i+z[i-zl+1]<=zr)z[i]=z[i-zl+1]; else{ z[i]=zr-i+1; while(i+z[i]<=s&&c[i+z[i]]==c[1+z[i]])z[i]++; zl=i;zr=i+z[i]-1; } } struct bitree{ int val[M+2],mn[M+1]; void init(){memset(val,0x3f,sizeof(val));memset(mn,0x3f,sizeof(mn));} void chkmn(int x,int v){ if(v>=val[x])return; val[x]=v; while(x<=m)mn[x]=min(mn[x],v),x+=lowbit(x); } int Mn(int x){ int res=inf; while(x)res=min(res,mn[x]),x-=lowbit(x); return res; } }bit[M+1],bitr[M+1]; int lft[M+1][M+1],rit[M+1][M+1]; int dp[M+1][M+1]; int main(){ cin>>b+1>>a+1; n=strlen(a+1);m=strlen(b+1); con(a,1,n,b,1,m);z_init(); for(int i=1;i<=m;i++)if(z[n+1+i]==n)cov[i][i+n-1]=true; for(int i=1;i<=m;i++){ con(b,i,m,b,1,m);z_init(); for(int j=1;j<=m;j++)lcP[i][j]=z[m-i+1+1+j]; } reverse(b+1,b+m+1); for(int i=1;i<=m;i++){ con(b,i,m,b,1,m);z_init(); for(int j=1;j<=m;j++)Lcs[m-i+1][m-j+1]=z[m-i+1+1+j]; } for(int i=1;i<=m;i++)bit[i].init(),bitr[i].init(); for(int i=m;i;i--)for(int j=i;j<=m;j++){ dp[i][j]=cov[i][j]?0:min(bit[j].Mn(i),bitr[i].Mn(m-j+1)); bit[j].chkmn(i-(lft[i][j]=max(lft[i][j-1],min(Lcs[j][i-1],j-i+1))),dp[i][j]+1); bitr[i].chkmn(m-(j+(rit[i][j]=max(rit[i+1][j],min(lcP[i][j+1],j-i+1))))+1,dp[i][j]+1); } cout<<(dp[1][m]==inf?-1:dp[1][m]); return 0; }Subtask :,数据随机。
留给乱搞。
Subtask :。
不妨换个思路,设 为区间 往左边(即头部)能粘贴的子串长度最大值, 为区间 往右边(即尾部)能粘贴的子串长度最大值。
如果求出了 和 ,我们可以通过一遍 BFS 求出答案。BFS 具体实现方法见代码,这里不再赘述。
-
如何预处理 ?
回忆一下 的定义:区间 往左/右能粘贴的子串长度最大值。如果认真思考,就能发现 一定不小于 ,因为区间 能往左粘贴的子串,区间 也一定能做到,其长度就具有单调性。 同理。合理运用单调性和字符串哈希,就能在 的时间内计算 。
预处理 时间复杂度 ,BFS 时间复杂度 。时间复杂度 。
-
应评论区补充一点:不难发现每次扩展的长度越大越好。感性理解一下,类似上面 一样,区间 能够扩展的长度一定不小于 或 。
-
另外也可以直接 DP 求答案,按照区间长度从小到大扩展即可。
#include<bits/stdc++.h> using namespace std; #define ull unsigned long long #define int short #define pii pair <int,int> #define fi first #define se second const int N=5e3+5; const int bs=131; ull hs[N],pw[N]; ull cal(int l,int r){return hs[r]-hs[l-1]*pw[r-l+1];} string s,t; int f[N][N],g[N][N],dp[N][N],n; queue <pii> q; signed main(){ cin>>t>>s,n=t.size(); if(s==t)puts("0"),exit(0); pw[0]=1; for(int i=1;i<=n;i++)pw[i]=pw[i-1]*bs; for(int i=0;i<n;i++)hs[i+1]=hs[i]*bs+(t[i]-'a'); for(int i=1;i<=n;i++){ int tmp=1; for(int j=i;j<=n;j++){ while(tmp<i&&j-tmp+1>=i&&cal(i-tmp,i-1)==cal(j-tmp+1,j))tmp++; f[i][j]=tmp-1; } tmp=1; for(int j=i;j;j--){ while(i+tmp<=n&&j+tmp-1<=i&&cal(j,j+tmp-1)==cal(i+1,i+tmp))tmp++; g[j][i]=tmp-1; } } int pos=t.find(s); if(pos==-1)puts("-1"),exit(0); while(pos!=-1)q.push({pos+1,pos+s.size()}),pos=t.find(s,pos+1); while(!q.empty()){ int l=q.front().fi,r=q.front().se,dl=f[l][r],dr=g[l][r],d=dp[l][r]; q.pop(); if(dl!=0){l-=dl; if(!dp[l][r])dp[l][r]=d+1,q.push({l,r}); l+=dl;} if(dr!=0){r+=dr; if(!dp[l][r])dp[l][r]=d+1,q.push({l,r}); r-=dr;} } cout<<(dp[1][n]?dp[1][n]:-1)<<endl; return 0; }
- 1
信息
- ID
- 5275
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者