华英蒟蒻杯内部赛∀x∀题解

题目传送门

题意:

给定一个数字 $N$ 和一个字符串 $S$ ,问满足如下条件的字符串有几个

  1. 长度为$N$
  2. 该字符串为回文串
  3. 该字符串的字典序 $\le$ $S$的字典序

多组测试数据,$N$的总和 $\le$ $10^6$

思路:

看完题,em...要用数学方法,我不会懒得想,其实这不就是数位DP吗

我们现在分别处理每个条件

  • 长度为$N$

这没啥好说的,就每一位单独处理

  • 回文串

既然他是个回文串,就说明我们只需要枚举前面一半,就可以知道后面的部分

  • 字典序 $\le$ $S$

就依据数位DP那样,用一个参数记录,当前枚举到的那位前面部分是否和 $S$ 相等,如果相等的话当前这一位就不能大过 $S$ 的这一位,否则就都可以。

举个栗子:

题目给的字符串$S$ : $abcdefghi$

当前枚举的字符串 :$abcde*???$

如果当前枚举到 $* $ 的位置,我们发现:

只要 $* $ 比 $f$ 大,那不管后面是什么都不可能使得当前枚举的字符串比 $S$ 小

如果 $* $ 就是 $f$ ,那就继续像这样判断下一位

如果 $* $ 比 $f$ 小,那后面是啥都不会使得当前枚举的字符串比 $S$ 大

但这时新问题出现了,如果我们前面一半和 $S$ 相等,但后面回文的那部分比 $S$ 大,那不就不合法了吗,所以我们只需要再开一个参数,记录后半部分是否等于或大于 $S$ ,这样就可以写代码了

代码:

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int t,n,ans,m,f[N][2][3];
const int mod=998244353;
char s[N];
int dfs(int k,int opt,int o){//记忆化搜索
    int q=0;
    if(k>m){
        if(!opt||o!=2) return 1;//满足条件返回一
        return 0;//否则是零
    }
    if(f[k][opt][o]!=-1) return f[k][opt][o];//记忆化
    for(int i='A';i<='Z';i++){
        if(opt&&i>s[k]) break;
        /*
        opt&&(i==s[k])意为:
            如果前面的部分相等并且枚举的这一位相等,那就继续相等
            否则为小于
        */
        if(o==1){//后半部分=S的情况
            if(i>s[n-k+1]) q+=dfs(k+1,opt&&(i==s[k]),2);
            else if(i==s[n-k+1]) q+=dfs(k+1,opt&&(i==s[k]),1);
            else q+=dfs(k+1,opt&&(i==s[k]),0);
        }
        else if(o==2){//后半部分>S的情况
            if(i>s[n-k+1]) q+=dfs(k+1,opt&&(i==s[k]),2);
            else if(i==s[n-k+1]) q+=dfs(k+1,opt&&(i==s[k]),2);
            else q+=dfs(k+1,opt&&(i==s[k]),0);
        }
        else{//后半部分<S的情况
            if(i>s[n-k+1]) q+=dfs(k+1,opt&&(i==s[k]),2);
            else if(i==s[n-k+1]) q+=dfs(k+1,opt&&(i==s[k]),0);
            else q+=dfs(k+1,opt&&(i==s[k]),0);
        }
        q%=mod;//取模
    }
    return f[k][opt][o]=q;//记忆化
}
int main(){
    scanf("%d",&t);
    while(t--){
        ans=0;
        scanf("%d",&n);
        m=(n+1)/2;
        scanf("%s",s+1);//读入
        for(int i=1;i<=m+2;i++){//清空记忆化数组
            f[i][0][0]=f[i][0][1]=f[i][0][2]=f[i][1][0]=f[i][1][1]=f[i][1][2]=-1;
        }
        ans=dfs(1,1,1);
        printf("%d\n",ans);//输出
    }
    return 0;
}
posted @ 2022-03-12 14:05  hubingshan  阅读(21)  评论(0)    收藏  举报  来源