I

发布时间 2023-09-18 22:09:36作者: TimMCBen

题意:

给出一个串,可能?、小写字母,大写字母、数字。

小写字母可以变成当前小写字母的小写或者大写形式。

数字和大写只能是当前形式。

问号可以变成另外三种形式。

现在要求相同的字符不相邻,且字符串中同时出现三种字符形式的方案数。

设dp[i][j][k],表示第i个位置为j,且当前状态为k的方案数。

k是状压的形式:[0 0 0]三个位置分别表示数字、小写和大写,其范围为[0,7]

当前的位置j通过映射,[1~62]对应[0 ~ 9, a ~ z, A ~ Z]

转移方程:

对于每个k和当前的h进行下列方程转移。

\[dp[i][h][st(h)|k]=\sum_{j=1\&\&j!=h}^{62}dp[i-1][j][k] \]

然而这样如果遇到问号,复杂度是\((k=7)*(j=62)*(当前的h=62)*(总长1e5)\)转移,过不了。

这里考虑先对于每个k,提前把\(\sum_{j=1}^{62}dp[i-1][j][k]\)处理好,然后对对应的h,特别的减去\(dp[i-1][h][k]\),写作下式:

\[dp[i][h][st(h)|k]=\sum_{j=1}^{62}dp[i-1][j][k]-dp[i-1][h][k] \]

则复杂度转化为\((k=7)*(j=62+当前的h=62)*(总长1e5)\)

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0)
#define int long long
const int N = 1e5+7;
int dp[2][80][10],tmp[10];
char ch[80];
map<char,int>mp;
const int mod = 998244353;
int st(int y)
{
	char x=ch[y];
	if(isdigit(x))
	{
		return 1;
	}
	if(islower(x))
	{
		return 2;
	}
	if(isupper(x))
	{
		return 4;
	}
    return 0;
}
signed main()
{
    IOS;
	int n;
	cin>>n;
	string s;
	cin>>s;
	s=' '+s;
	int cnt=0;
	for(int i=0;i<=9;i++)
	{
		ch[++cnt]=i+'0';
		mp[i+'0']=cnt;
	}
	for(char x='a';x<='z';x++)
	{
		ch[++cnt]=x;
		mp[x]=cnt;
	}
	for(char x='A';x<='Z';x++)
	{
		ch[++cnt]=x;
		mp[x]=cnt;
	}
	int id=mp[s[1]];
	if(st(id)==0)
	{
        //问号
		for(int i=1;i<=62;i++)
		{
			dp[1][i][st(i)]=1;
		}	
	}else if(st(id)==1)
	{
        //数字
		dp[1][id][st(id)]=1;	 
	}else if(st(id)==2) 
	{
		//小写 
		dp[1][id][st(id)]=1;
		dp[1][id+26][st(id+26)]=1;
	}else if(st(id)==4)
	{
		//大写 
		dp[1][id][st(id)]=1;
	}
	int cur=1;
	for(int i=2;i<=n;i++)
	{
		for(int k=1;k<=7;k++)
		{
			tmp[k]=0;
		}
		for(int j=1;j<=62;j++)
		{
			for(int k=1;k<=7;k++)
			{
                //把所有k相同的状态做前缀和转移
				tmp[k]+=dp[cur][j][k];
                tmp[k]%=mod;
                //滚动数组清空
                dp[cur^1][j][k]=0;
            }
		}
        int now=mp[s[i]];
		for(int k=1;k<=7;k++)
		{
			if(st(now)==0)
			{
				for(int id=1;id<=62;id++)
				{
					//问号的时候,枚举当前
                    dp[cur^1][id][k|st(id)]+=tmp[k]-dp[cur][id][k];
                    dp[cur^1][id][k|st(id)]=(dp[cur^1][id][k|st(id)]%mod+mod)%mod;
				}	
			}else if(st(now)==1)
			{
				//[1,10]
				int id=mp[s[i]];
				dp[cur^1][id][k|st(id)]+=tmp[k]-dp[cur][id][k];
				dp[cur^1][id][k|st(id)]=(dp[cur^1][id][k|st(id)]%mod+mod)%mod;
			
			}else if(st(now)==2) 
			{
				//小写 
			
				int id=mp[s[i]];
				dp[cur^1][id][k|st(id)]+=tmp[k]-dp[cur][id][k];
				dp[cur^1][id][k|st(id)]=(dp[cur^1][id][k|st(id)]%mod+mod)%mod;

				id+=26;
				dp[cur^1][id][k|st(id)]+=tmp[k]-dp[cur][id][k];
				dp[cur^1][id][k|st(id)]=(dp[cur^1][id][k|st(id)]%mod+mod)%mod;
			
				
			}else if(st(now)==4)
			{
				//大写 
				int id=mp[s[i]];
				dp[cur^1][id][k|st(id)]+=tmp[k]-dp[cur][id][k];
				dp[cur^1][id][k|st(id)]=(dp[cur^1][id][k|st(id)]%mod+mod)%mod;
			}		
		} 
        cur=cur^1;
	}
	int maxn=0;
	for(int i=1;i<=62;i++)
	{
		maxn=maxn+dp[cur][i][7];
		maxn%=mod;
	}	
	cout<<maxn<<endl;
}