题解:P3082 [USACO13MAR] Necklace G

· · 题解

模拟赛考了这题,依旧被创飞。

看到题目我首先想到贪心有以下三种策略:

但是显然对于长度为 2 的时候,你不好判断怎么去删,是删前面的,还是删后面的。

观察一下题目的数据范围 N\le 10000M\le 1000 考虑动态规划,我们设 dp_{i,j} 为考虑了 N 的前 i 位,M 的前 j 位,能剩余的字符串最长长度,那么关于 dp_{i,j} 的转移显然有:

好了,现在我们考虑怎么快速求出 $k$,一看到字符串匹配我们考虑 KMP,我们考虑预处理出一个 $nxtc_{i,c}$ 表示第 $i$ 个位置选 $N_i$ 能匹配到的 $M$ 的最大长度,于是考虑一下怎么转移,观察 KMP 的原理很容易想到: - 如果 $M_{i+1}=c$ 那么: $$nxtc_{i,c}=i+1$$ - 否则就是更具 $nxt_i$(注意没有第二维)匹配不到的转移选择次优的继续匹配,那么转移为: $$nxt_{i,c}=nxtc_{nxt_i,c}$$ 于是我们一遍跑 KMP 一遍预处理出 $nxtc$ 再用动态规划求出能剩余的字符串最长长度,则答案为: $$n-\max\{dp_{n,i}\}$$($n$ 为 $N$ 的长度) 时间复杂度:$\Theta(nm)$。 空间复杂度:$\Theta(nm)$。 :::success[[Ac Code](https://www.luogu.com.cn/record/243851942)] ```cpp line-numbers #include <bits/stdc++.h> using namespace std; #ifdef __linux__ #define gc getchar_unlocked #define pc putchar_unlocked #else #define gc _getchar_nolock #define pc _putchar_nolock #endif #define int long long #define rint register int #define R register #define _ read<int>() template<class T>inline T read() { T r=0,f=1;R char c=gc(); while(!isdigit(c)) { if(c=='-') f=-1; c=gc(); } while(isdigit(c)) r=(r<<1)+(r<<3)+(c^48),c=gc(); return f*r; } inline void out(rint x) { if(x<0) pc('-'),x=-x; if(x<10) pc(x+'0'); else out(x/10),pc(x%10+'0'); } const int MAXN=1e4+10,MAXM=1e3+10; int dp[MAXN][MAXM],nxt[MAXM],nxtc[MAXM][30]; signed main() { string N,M; cin>>N>>M; rint n=N.size(),m=M.size(); N=' '+N,M=' '+M; for(rint i=2,j=0;i<=m;i++)//对M跑一遍KMP { while(j>0&&M[i]!=M[j+1]) j=nxt[j]; if(M[i]==M[j+1]) j++; nxt[i]=j; } for(rint i=0;i<=m;i++)//求出nxtc { for(rint j=0;j<26;j++)//枚举字符 { nxtc[i][j]=(M[i+1]==j+'a')?i+1:nxtc[nxt[i]][j]; } } memset(dp,-0x3f,sizeof(dp)); dp[0][0]=0; for(rint i=1;i<=n;i++)//dp { for(rint j=0;j<m;j++)//j=0是空串j=m不能转移 { if(dp[i-1][j]<0) continue; dp[i][j]=max(dp[i][j],dp[i-1][j]); rint tmp=nxtc[j][N[i]-'a']; if(tmp<m) dp[i][tmp]=max(dp[i-1][j]+1,dp[i][tmp]); } } rint ans=0; for(rint i=0;i<m;i++) ans=max(ans,dp[n][i]); out(n-ans); return 0; } ``` :::