学到了子集dp!!
考虑枚举中心点往外扩,
会遇到三种情况:
1.ac** 后面两种**是唯一确定的,如果要继续往外扩充则必须满足ac在其子集内。
2.**** 方案|s/2|
3.ac这种情况是不考虑的直接continue
对于其中扩充到的,或者说转移中的每一种状态都对应有一个子集b,为其必须包括的子集,
我们先统计每个子集的dp值。
再进行子集dp ,对于某个小子集 的贡献, 一定 也是属于它的大子集的贡献,换句话说,假如我需要abc, 但我给你abcd 一定能满足 你的条件。
for(int j=1 ; j <= 17 ; j++ ){
for(int st = 1; st <= (1<<17)-1 ; st++ ){
if(st&(1<<(j-1))){
for(int k=1 ; k <=17 ; k++ ){
f[k][st] = ( f[k][st] + f[k][st^(1<<(j-1))] )%mod;
}
}
}
}
ac代码:
#include <stdio.h>
#include <cstring>
typedef long long ll;
const int mod = 998244353;
char s[1010];
int cnt=0,n;
ll f[19][(1<<17)+5];
ll ksm(ll a,ll n) {
ll ans=1;
while(n){
if(n&1)ans = ans*a %mod;
a = a*a %mod;
n>>=1;
}
return ans;
}
void solve(int l,int r, int count){
int res = 0;
int sta = 0;
for( ; l>=1&&r<=n; l--,r++) {
if( s[l]==s[r] && s[l]=='?')res++,count-=(2-(l==r));
else if( s[l] == s[r] );
else if( s[l] == '?' ) sta |= (1<<(s[r]-'a')),count--;
else if( s[r] == '?' ) sta |= (1<<(s[l]-'a')),count--;
else break;
for(int i = 1 ; i <= 17 ; i++ ){ // 要实时统计答案 * ** *
// cout<< ksm(i,count+res)<<endl;
f[i][sta] = (f[i][sta] + ksm(i,count+res) + mod)%mod;
}
}
}
int main() {
scanf("%d",&n);
scanf("%s",s+1);
for( int i=1 ; i <= n ; i++){
if(s[i] == '?') cnt++;
}
for( int i = 1 ; i <= n; i++){
solve(i,i,cnt);
solve(i-1,i,cnt);
}
for(int j=1 ; j <= 17 ; j++ ){
for(int st = 1; st <= (1<<17)-1 ; st++ ){
if(st&(1<<(j-1))){
for(int k=1 ; k <=17 ; k++ ){ //也可以在第一层循环
f[k][st] = ( f[k][st] + f[k][st^(1<<(j-1))] )%mod;
}
}
}
}
int q;
scanf("%d",&q);
for( int i=1 ; i <= q ; i++){
int st = 0;
char ss[20];
scanf("%s",ss+1);
int len = strlen( ss +1 ) ;
// cout<<" len = "<<len<<endl;
for( int j=1 ; j <= len; j++ ){
st |= (1<<(ss[j]-'a'));
}
printf("%lld\n",f[len][st]);
}
return 0;
}