[ABC359D]Avoid K Palindrome
错误思路一:
对于非?的字符,枚举,枚举范围是[i,i+k-1],找到所有的构成回文串的数量,用总数量减去构成回文串的数量,就是非回文串的数量,把这个加到答案里面,但是这个思路也是错的,为什么?因为这样会造成重复,这样只能保证枚举的范围里面没有回文串,如果枚举到下一个范围,这个范围的非回文会和上一个范围的非回文构成相同的子串,譬如ABAB.....BBAA,这个字符串是合法的,他在ABAB时会被枚举一次,在BBAA时仍然会被枚举一次,所以会造成重复
错误思路二:
设定\(f[i]\)表示前i个串能构成好串的数量,那么「i-k+1,i」要构成好串(不含回文),这个我们可以dfs查找,然后我们可以得到\(f[i]=f[i-k]+g(i-k+1,i)构成的好串的数量\),这样看似是正确的转移方程,其实是错误的,我可以举个例子来看
前i-k个以ABAB结尾,「i-k+1,i」是BABA这样的字符串,两边都是不能构成回文串的,但是当我们把他们拼接在一起的时候,他是可以构成回文串的ABABBABA,所以这样的思路是错误的,同样的,我对这个错误的思路进行一个补充,不止是对称的情况,还存在不对称的情况,也会出去连接的时候出现回文串,还有一个很重要的原因是,对于[i-k+1,i]这个区间,我只是求数量,并没有求是哪些串,所以我在拼接的时候根本无法进行,所以即使我修改状态为f[i][c]前i个串以c结束的好串的数量,也不行,因为我在拼接的时候,只计算了数量,没有描绘每个字符串的形态
正确思路:
我看到k<=10,当时我没有和状压DP挂上钩,说明状压DP的题做的比较少,其实这个题看到k<=10,同时看到只有A和B这两种字符,我们可以快速思考到状压DP,如果已经告知你是状压DP了,DP状态应该怎么设置呢?这个就比较自然
\(f[i][j]\)表示前i个中k个字符结束构成j能表示的好串的数量,那么\(f[i][j]=f[i][j]+f[i-1][k]\),当然,他需要满足如下条件:
假设A表示1,B表示0,那么如果s[i]='A',\((k<<1)|1==j\),如果是\(s[i]='B',(k<<1)==j\),在这里,有一个小细节需要注意一下,左移一位并不能直接左移,这里指的是在k位内左移,所以这里面有个细节很重要。如果s[i]='?'怎么办?因为?号可以取‘A’,也可以取‘B’,所以两种情况下都可以转移
第二,还有一个地方需要处理,我们要求k个子串不能是回文,所以对于每个i结束的k个子串,我们都需要处理一下哪些能用,哪些不能用,我用dfs处理的
最终的代码里,我并没有枚举k,因为k可以通过j位运算得到,所以我省了一层枚举
最终代码如下:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,k;
char s[1005];
int cnt,st,dp[1005][15000],ans;
int mod=998244353;
bool f[1500],a[1005][1500];
bool check(int x){
int g=0;
int a[20];
while(x){
a[++g]=x%2;
x=x/2;
}
while(g<=k){
a[++g]=0;
}
int d=k;
for(int i=1;i<=k/2;i++)
if(a[i]!=a[d]){
return true;
}else{
d--;
}
return false;
}
void dfs(int p,int py,int x){
if(p==st-k){
if(f[x]==true)
a[st][x]=true;
return;
}
if(s[p]=='B'){
dfs(p-1,py+1,x);
}
if(s[p]=='A'){
x=x|(1<<py);
dfs(p-1,py+1,x);
}
if(s[p]=='?'){
dfs(p-1,py+1,x);
x=x|(1<<py);
dfs(p-1,py+1,x);
}
}
int main(){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
for(int i=0;i<((1<<k)-1);i++){
if(check(i)) f[i]=true;
}
for(int i=k;i<=n;i++){
cnt=0;
st=i;
dfs(st,0,0);
a[i][0]=cnt;
}
for(int i=0;i<((1<<k)-1);i++){
if(a[k][i])
dp[k][i]=1;
}
for(int i=k+1;i<=n;i++){
for(int j=0;j<((1<<k)-1);j++){
if(a[i][j]==true){
if(s[i]=='A'||s[i]=='?'&&(j&1)==1){
int sk=(j>>1);
if(a[i-1][sk]==true) dp[i][j]=(dp[i][j]+dp[i-1][sk])%mod;
sk=(j>>1)|(1<<(k-1));
if(a[i-1][sk]==true) dp[i][j]=(dp[i][j]+dp[i-1][sk])%mod;
}
if(s[i]=='B'||s[i]=='?'&&(j&1)==0){
int sk=(j>>1);
if(a[i-1][sk]==true) dp[i][j]=(dp[i][j]+dp[i-1][sk])%mod;
sk=(j>>1)|(1<<(k-1));
if(a[i-1][sk]==true) dp[i][j]=(dp[i][j]+dp[i-1][sk])%mod;
}
}
}
}
for(int i=0;i<((1<<k)-1);i++){
if(a[n][i])
ans=(ans+dp[n][i])%mod;
}
printf("%d",ans);
return 0;
}
/**
7 4
AB?A?BA
**/