题目
对于一个包含字符 \(\{R,P,S\}\) 的字符串 \(s\),满足每相邻两个字符都不同且 \(s_1=s_n=R\)。
你可以做两种操作:
- 找出第一个 \(RS\) 或 \(SR\) 将其替换为 \(R\)。
- 找出第一个 \(SP\) 或 \(PS\) 将其替换为 \(S\)。
若无法进行任意一个操作则结束,否则继续执行,计操作序列为 \(b\)。
给你一个长为 \(n\) 的字符串 \(s\) 包含 \(\{R,P,S,?\}\),你可以将 \(?\) 替换为 \(\{R,P,S\}\) 中的一个使得其字符串满足上述条件,并对其进行操作得到 \(b\),问有多少方案,两个方案不同当且仅当 \(s\) 和 \(s'\),\(b\) 和 \(b'\) 至少有一个不同。\(n\leq 200\)
题解
首先 \(R\) 是无法删除的,那么我们希望用 \(R\) 来把问题分成若干段。注意到,我们每次删除 \(P\) 的时候,一定是删除最开头的 \(P\)(除 \(RPR\) 这唯一一个特例删不掉 \(P\))。但 \(S\) 就没有这么好的性质了。
所以设 \(f[i][j][k]\) 表示考虑前 \(i\) 个位置,第 \(i\) 个位置是 \(R\),且第 \(i\) 个位置前面有 \(j\) 个 \(S\) 未被删除(这 \(j\) 个 \(S\) 不被 \(P\) 包裹),后面有 \(k\) 个 \(S\) 要删除时的答案。转移时,枚举下一个 \(R\) 的位置,那么中间的部分一定是 \(S\) 和 \(P\) 交替出现。
肯定没法直接转移到因为中间的 \(S\) 可能会被 \(P\) 包裹而不能直接删掉,不满足 \(f\) 的状态定义(例如 \(RPSPR\)),所以可以考虑再记录另一个 \(dp\) 为 \(g[i][l][j][k][0/1][0/1]\) ,其中 \(i,j,k\) 与上面类似,\(l\) 表示中间的部分里 \(P\) 还有几个,中间的部分的 开头\(/\)结尾 不是\(/\)是 \(S\)(\(1\) 表示是 \(S\))。
即 \(f,g\) 定义的直观解释如:
还有一个问题就是考虑 \(RSPSPSR\) 删掉一个 \(P\) 后就会变成 \(RSSPSR\),导致它中间 \(S,P\) 不再交替,这个形式是没有状态来记录的。但是我们可以将他微调为 \(SRSPSR\),即把第一个 \(S\) 交换到 \(R\) 的前面,那这个就有状态就可以记录了,而且它仍然满足可以直接删掉。
为了方便我们写代码和转移,我们可以稍微改一下 \(g\) 的状态,我们让中间那段的左端必须是 \(P\),前面一旦出现 \(S\) 就扔到 \(R\) 左边,并将第一个 \(0/1\) 改为记录其左端 \(R\) 的左边还有没有 \(S\)。
转移比较多,可以自己先推一下试试,但其实还是有点难度的。
转移
对于 \(f[j]*\) 对 \(f[i]*,g[i]*\) 的转移:
枚举上一个 \(R\) 的位置 \(j\) 及其状态,枚举中间段的左右端点是 \(fl,fr\),可以算得中间段 \(P\) 的个数是 \(p=\frac{i-j-fl-fr}{2}\):
- 特殊处理 \(RSR\) 的情况:
- \(f[i][k][l-1]\gets f[j][k][l],l\geq 1\)(因为 \(l\geq 1\) 所以这个 \(S\) 要删。)
- \(f[i][k+1][l]\gets f[j][k][l],l==0\)(\(l==0\) 这个 \(S\) 不用删。)
- 否则有转移:
- \(g[i][p][k+fl][t][fl][fr]\gets f[j][k][l]\),对于 \(j\) 位置需要往后删掉 \(l\) 个 \(S\),那么 \([j,i]\) 这一段中目前能直接删掉的 \(S\) 就是开头和结尾的两个(如果他们是 \(S\) 的话)所以如果 \(t\geq 1,fl=1\) 则 \(t--,fl=0\),\(fr\) 同样(初始 \(t=l\))。
对于 \(g[x]*\) 和 \(f[x]*\) 的转移:
- 特判 \(RPR\) 的情况 \(f[x][j][k]\gets g[x][i][j][k][fl][fr]\)
- 删除一个 \(S\):
- \(g[x][i][j-1][k][fl][fr]\gets g[x][i][j][k][fl][fr],j>1\)
- \(g[x][i][0][k][0][fr]\gets g[x][i][j][k][fl][fr],j==1\)(如果左边只剩一个 \(S\),删除后左边没有 \(S\),\(fl=0\))
- \(g[x][i][0][k][0][0]\gets g[x][i][0][k][0][1]\)(左边没有能删的 \(S\) 了,那删除 \(fr\) 处的 \(S\))
- \(g[x][i][0][k+1][0][0]\gets g[x][i][0][k][0][0]\)(目前已经没有 \(S\) 了,那么 \(k+1\),告诉后面还要多删一个 \(S\))
- 删除一个 \(P\):一定删掉了最左边的 \(P\),那么他一定会新露出一个 \(S\),我们把他直接扔到 \(R\) 左边。注意当中间的 \(P\) 只剩一个的时候就已经不存在被 \(P\) 包裹的 \(S\) 了,所以要 \(i>1\)。\(g[x][i-1][j+1][k][1][fr]\gets g[x][i][j][k][fl][fr],i>1\)
- 当 \(P\) 只剩一个的时候,更新答案 \(f[x][j+fr][k]\gets g[x][i][j][k][fl][fr],i==1\)
转移就这么多。
\(f\) 的状态数有三维,转移 \(O(n)\)。\(g\) 的状态数有四维,转移 \(O(1)\),所以时间复杂度为 \(O(n^4)\)。
\(g\) 数组可以滚动,空间复杂度为 \(O(n^3)\)。常数很小,可以通过。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;inline ll read(){ll s=0,k=1;char c=getchar();while(c>'9'||c<'0'){if(c=='-') k=-1;c=getchar();}while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+(c^48);c=getchar();}return s*k;
}const int N=1005,M=205,mod=998244353;
char s[N];
int n;
ll g[M][M][M][2][2],f[M][M][M];void Add(ll &x,ll y){x+=y;if(x>=mod) x-=mod;
}bool ok(char x,int y){if(x=='?') return 1;if(y==1&&x!='S') return 0;if(y==0&&x!='P') return 0;return 1;
}void clear(int n,int m,int K){for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)for(int k=0;k<=K;k++)for(int fll=0;fll<2;fll++)for(int flr=0;flr<2;flr++) g[i][j][k][fll][flr]=0;
}void solve(ll f[M][M],int n,int m,int K){for(int i=n;i>=1;i--)for(int j=m;j>=0;j--)for(int k=0;k<=K;k++)for(int fll=1;fll>=0;fll--)for(int flr=1;flr>=0;flr--)if(g[i][j][k][fll][flr]){ll &v=g[i][j][k][fll][flr];if(i==1&&!fll&&!flr){Add(f[j][k],v);continue;}if(j==1) Add(g[i][0][k][0][flr],v);else if(j>1) Add(g[i][j-1][k][fll][flr],v);else if(flr) Add(g[i][j][k][fll][0],v);else Add(g[i][j][k+1][fll][flr],v);if(i>1) Add(g[i-1][j+1][k][1][flr],v);else Add(f[j+flr][k],v);}
}int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);scanf("%s",s+1);n=strlen(s+1);f[1][0][0]=1;for(int i=2;i<=n;i++){if(s[i]!='?'&&s[i]!='R') continue;clear(i>>1,i>>1,n-i+1>>1);for(int flr=0;flr<2;flr++)for(int j=i-2;j>=1;j--){int fll=i-j-2+flr&1;if(!ok(s[j+1],fll)) break;for(int k=0;k<=j>>1;k++)for(int l=0;l<=n-j+1>>1;l++)if(f[j][k][l]){if(j==i-2&&flr){if(l) Add(f[i][k][l-1],f[j][k][l]);else Add(f[i][k+1][l],f[j][k][l]);continue;} int t=l,p=i-j+1-fll-flr-1>>1;int fl0=fll,fl1=flr;if(t&&fl0) t--,fl0--;if(t&&fl1) t--,fl1--;Add(g[p][k+fl0][t][fl0][fl1],f[j][k][l]);}}solve(f[i],i>>1,i>>1,n-i+1>>1);}ll ans=0;for(int i=0;i<=n;i++) Add(ans,f[n][i][0]);printf("%lld",ans);return 0;
}