在看这道题前,有必要先看一下DP套DP的入门题[uoj3864]Hero meet devil,附上两篇写得不错的题解:
https://blog.youkuaiyun.com/Ike940067893/article/details/87863041
https://www.cnblogs.com/RabbitHu/p/BZOJ3864.html
- 可以发现, S S S和 T T T相似,等价于它们的最长公共子序列长度至少为 n − k n-k n−k
- 考虑如何求两个字符串的LCS。设
l
c
s
i
,
j
lcs_{i,j}
lcsi,j 表示
S
[
1...
i
]
S[1...i]
S[1...i] 与
T
[
1...
j
]
T[1...j]
T[1...j] 的 LCS 长度,转移显然为
l c s i , j = { l c s i − 1 , j − 1 + 1 ( S [ i ] = = T [ j ] ) m a x { l c s i − 1 , j , l c s i , j − 1 } ( S [ i ] ! = T [ j ] ) lcs_{i,j}=\left\{ \begin{aligned} &lcs_{i-1,j-1}+1(S[i]==T[j]) \\ & max\{lcs_{i-1,j},lcs_{i,j-1}\} (S[i]!=T[j])\\ \end{aligned} \right. lcsi,j={lcsi−1,j−1+1(S[i]==T[j])max{lcsi−1,j,lcsi,j−1}(S[i]!=T[j]) - 考虑如何拓展到 T T T任意的情况。自然的想法是DP套DP:设 d p [ j ] [ l c s 1 , j , l c s 2 , j , . . . , l c s n , j ] dp[j][lcs_{1,j},lcs_{2,j},...,lcs_{n,j}] dp[j][lcs1,j,lcs2,j,...,lcsn,j]表示 考虑到 T T T的前 j j j位, l c s − , j lcs_{-,j} lcs−,j的情况如第二维所示 的方案数
- 直接这样维护状态肯定是行不通的,考虑优化:
1.发现 l c s i , j − l c s i − 1 , j ∈ { 0 , 1 } lcs_{i,j}-lcs_{i-1,j}\in\{0,1\} lcsi,j−lcsi−1,j∈{0,1},那么 l c s 1 , j , l c s 2 , j , . . . , l c s n , j lcs_{1,j},lcs_{2,j},...,lcs_{n,j} lcs1,j,lcs2,j,...,lcsn,j的差分数组必是一个01 串 ,我们 状压这个01串 来描述第二维的状态
2. n n n位01串还是太大了,考虑如何利用 k k k很小这个性质进行优化。
考 虑 某 个 l c s i , j , 若 ∣ i − j ∣ > k , 那 么 在 转 移 到 l c s n , n 时 , 必 然 值 不 超 过 l c s i , j + m i n { n − i , n − j } < = m i n { i , j } + m i n { n − i , n − j } = n − ∣ i − j ∣ < n − k \color{Red}{考虑某个lcs_{i,j}},若|i-j|>k,那么在转移到lcs_{n,n}时,必然值不超过lcs_{i,j}+min\{n-i,n-j\}<=min\{i,j\}+min\{n-i,n-j\}=n-|i-j|<n-k 考虑某个lcsi,j,若∣i−j∣>k,那么在转移到lcsn,n时,必然值不超过lcsi,j+min{n−i,n−j}<=min{i,j}+min{n−i,n−j}=n−∣i−j∣<n−k
因此这样的 l c s i , j lcs_{i,j} lcsi,j是没用的,可以直接丢掉,即对于每个 j j j,我们只需要记录 l c s j − k , j , l c s j − k + 1 , j , . . . , l c s j + k , j lcs_{j-k,j},lcs_{j-k+1,j},...,lcs_{j+k,j} lcsj−k,j,lcsj−k+1,j,...,lcsj+k,j
3.只知道 l c s j − k , j lcs_{j-k,j} lcsj−k,j~ l c s j + k , j lcs_{j+k,j} lcsj+k,j的差分数组,我们是无法知道它们的实际值的,因此考虑开多一维来记录 l c s j , j lcs_{j,j} lcsj,j的实际值以推出其它的 l c s lcs lcs值,即我们设 d p [ j ] [ x ] [ s ] dp[j][x][s] dp[j][x][s]表示 考虑到 T T T的前 j j j位, l c s j , j = = x lcs_{j,j}==x lcsj,j==x, l c s j − k , j , l c s j − k + 1 , j , . . . , l c s j + k , j lcs_{j-k,j},lcs_{j-k+1,j},...,lcs_{j+k,j} lcsj−k,j,lcsj−k+1,j,...,lcsj+k,j的差分数组状压后为 s s s 的方案数
4. ∵ l c s n , n ∈ [ n − k , n ] ( 即 不 匹 配 的 字 符 最 多 k 个 ) \because lcs_{n,n}\in [n-k,n](即不匹配的字符最多k个) ∵lcsn,n∈[n−k,n](即不匹配的字符最多k个)
∴ ∀ j ∈ [ 1 , n ] 满 足 l c s j , j ∈ [ j − k , j ] \therefore\forall j\in[1,n]满足lcs_{j,j}\in [j-k,j] ∴∀j∈[1,n]满足lcsj,j∈[j−k,j],所以我们可以将 d p dp dp的第二维设置为 l c s j , j − ( j − k ) = l c s j , j − j + k lcs_{j,j}-(j-k)=lcs_{j,j}-j+k lcsj,j−(j−k)=lcsj,j−j+k,即 d p [ ] [ x ] [ ] dp[][x][] dp[][x][]表示 l c s j , j − j + k = = x lcs_{j,j}-j+k==x lcsj,j−j+k==x,这样就令 x ∈ [ 0 , k ] x\in[0,k] x∈[0,k] - 考虑如何实现状态转移。 d p dp dp数组的第三维不好直接得到,我们考虑把它预处理出来:设 p [ i ] [ j ] [ s 1 ] [ c ] p[i][j][s1][c] p[i][j][s1][c]表示考虑到 T T T的前 i i i位, l c s i , i = = j lcs_{i,i}==j lcsi,i==j(知道 l c s i , i lcs_{i,i} lcsi,i就可以通过差分数组推出其它的 l c s lcs lcs值), l c s i − k , i , l c s i − k + 1 , i , . . . , l c s i + k , i lcs_{i-k,i},lcs_{i-k+1,i},...,lcs_{i+k,i} lcsi−k,i,lcsi−k+1,i,...,lcsi+k,i的差分数组状压后为s1,在 T T T的 i + 1 i+1 i+1位新增字符 c c c后, l c s i + 1 − k , i + 1 , l c s i + 2 − k , i + 1 , . . . , l c s i + 1 + k , i + 1 lcs_{i+1-k,i+1},lcs_{i+2-k,i+1},...,lcs_{i+1+k,i+1} lcsi+1−k,i+1,lcsi+2−k,i+1,...,lcsi+1+k,i+1状压完的差分数组
- 同样,需要优化
p
p
p的状态设置:
1.同上,我们可以将 p p p的第二维设置为 l c s i , i − ( i − k ) = l c s i , i − i + k lcs_{i,i}-(i-k)=lcs_{i,i}-i+k lcsi,i−(i−k)=lcsi,i−i+k,即 p [ ] [ j ] [ ] [ ] p[][j][][] p[][j][][]表示 l c s i , i − i + k = = j lcs_{i,i}-i+k==j lcsi,i−i+k==j,这样就令 j ∈ [ 0 , k ] j\in[0,k] j∈[0,k]
2.其实 p p p转移到的新差分数组状态与新增字符具体是什么无关,与新增字符与S每一位的匹配情况有关。再进一步地,
∵ l c s h , h ∈ [ h − k , h ] \because lcs_{h,h}\in [h-k,h] ∵lcsh,h∈[h−k,h]
∴ T [ h ] 只 可 能 与 S [ h − k ] \therefore T[h]只可能与 S[h-k] ∴T[h]只可能与S[h−k]~ S [ h + k ] 匹 配 S[h+k] 匹配 S[h+k]匹配,即我们只需要考虑新增字符与 S [ i + 1 − k ] S[i+1-k] S[i+1−k]~ S [ i + 1 + k ] S[i+1+k] S[i+1+k]的匹配情况。直接把第四维设置成这个匹配情况即可。
3.仔细想想,把第四维改成匹配情况后, p p p的转移已经与第一维无关了,直接删掉即可
至此,这道题就可以实现了
#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 998244353
using namespace std;
typedef pair<int,int> pr;
const int N=30001;
const int K=5;
const int S=256;
int n,m,ans,dp[N][K][S],f[31],g[31];
pr p[K][S][S<<1];
char ch[N];
bool flag[31];
int add(int a,int b){
a=a+b;
if(a>mod) a-=mod;
return a;
}
int mul(int a,int b){
return 1ll*a*b%mod;
}
void dfs(int a,int b,int c){
if(dp[a][b][c]==0) return;
int l=max(a-m,0),r=min(a+m,n-1),sz=26;
for(int i=l;i<=r;i++){
if(!flag[ch[i+1]-'A']){
flag[ch[i+1]-'A']=1;
sz--;
}
f[ch[i+1]-'A']|=(1<<(i-a+m));//统计每种字符的匹配情况
}
for(int i=l;i<=r;i++){
if(flag[ch[i+1]-'A']){
int x=p[b][c][f[ch[i+1]-'A']].first;
int y=p[b][c][f[ch[i+1]-'A']].second;
if(x>=0) dp[a+1][x][y]=add(dp[a+1][x][y],dp[a][b][c]);
flag[ch[i+1]-'A']=0;
f[ch[i+1]-'A']=0;
}
}
int x=p[b][c][0].first,y=p[b][c][0].second;
if(x>=0) dp[a+1][x][y]=add(dp[a+1][x][y],mul(dp[a][b][c],sz));
}
int main(){
scanf("%s%d",ch+1,&m);
n=strlen(ch+1);
//省略一维l,表示考虑到第l位
for(int i=0;i<=m;i++){//lcs[l][l]-l+m==i
for(int j=0;j<(1<<(2*m));j++){//lcs[l-m][l]~lcs[l+m][l]的差分数组状压
for(int k=0;k<(1<<(2*m+1));k++){//T[l+1]与S[l+1-m]~S[l+1+m]匹配情况状压
//f:(lcs[l-m][l]-l+m)~(lcs[l+m][l]-l+m)
//g:(lcs[l+1-m][l+1]-l+m)~(lcs[l+1+m][l+1]-l+m)
f[m]=i;
for(int a=m-1;a>=0;a--) f[a]=f[a+1]-(j>>a&1);
for(int a=m;a<2*m;a++) f[a+1]=f[a]+(j>>a&1);
for(int a=0;a<=2*m;a++){
if((k>>a)&1) g[a+1]=max(f[a]+1,f[a+1]);//匹配
else g[a+1]=max(f[a],f[a+1]); //不匹配
g[a+1]=max(g[a],g[a+1]);
}
int x=g[m+1]-1;//x=lcs[l+1][l+1]-(l+1)+m
int y=0;
if(x>=0){
for(int a=m-1;a>=0;a--)
if(g[a+1]^g[a+2]) y|=1<<a;
for(int a=m;a<2*m;a++)
if(g[a+1]^g[a+2]) y|=1<<a;
}
p[i][j][k]=make_pair(x,y);
}
}
}//预处理
for(int i=0;i<=30;i++) f[i]=0;
dp[0][m][0]=1;
for(int i=0;i<n;i++){//考虑到第i位
for(int j=0;j<=m;j++){//lcs[i][i]-i+m==j
for(int k=0;k<(1<<(2*m));k++){//差分数组状压
dfs(i,j,k);
}
}
}//dp
for(int i=0;i<=m;i++){
for(int j=0;j<(1<<(2*m));j++){
ans=add(ans,dp[n][i][j]);
}
}
printf("%d\n",ans);
return 0;
}