最终状态自左至右一定形如
<<<===>>>
,即中间有一段和原序列相等,左边都是左箭头,右边都是右箭头的形式。
证明考虑如果要保留原序列 \([l,r]\) 一段(显然 \([l,r]\) 中不含 .
),那么设位于 \(l\) 以左且距 \(l\) 最近的前两个点为 \(i,j\)(满足 \(i>j\)),如果操作方案中 \(i\) 位于 \(j\) 之前,\(i\) 只能向左走,这样 \(j\) 就成了最近的点且有一段合法后缀,反之如果 \(i\) 在 \(j\) 之后,那么 \(j\) 的操作对合法性没有任何影响。所以最终必然只能形成一段左箭头组成的前缀,后缀同理。
于是我们只需要枚举不变的连续段(对于不存在的情况可以通过在串首尾加 .
来解决),这样前后就是两个独立且等价的问题。对于左面,即是求随机操作至结束不触碰右边界的概率,右边的问题与之对称。
注意到概率实际只与 .
的个数相关,不妨设 \(f_i\) 为上述问题在 .
个数为 \(i\) 时的答案,则有 \(f_i=f_{i-1} \times (1-\frac{1}{2i})\)。式子的意义是除去第一次就触碰边界的情况,其余情况都能递归到下一层。而每种情况被递归到的概率是相等的。
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef double db;
inline ll read() {
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const ll N=1e5+5,p=998244353;
int n,pre[N],L[N],R[N],cnt;
ll inv[N],f[N];
string s;
void init() {
inv[0]=inv[1]=f[0]=1;
for(int i=2;i<=n;i++) inv[i]=(p-p/i)*inv[p%i]%p;
for(int i=1;i<=n;i++) f[i]=f[i-1]*(p+1-inv[2]*inv[i]%p)%p;
}
int main() {
n=read(),cin>>s;s+=".";s="."+s;
init();int lst=-1;
for(int i=0;i<=n+1;i++) {
pre[i]=pre[i-1]+(s[i]=='<');
if(s[i]=='.') {
if(lst!=-1) L[++cnt]=lst,R[cnt]=i;
lst=i;
}
}
ll ans=0;
for(int i=1;i<=cnt;i++) (ans+=1ll*(pre[R[i]]-pre[L[i]]+L[i])*f[i-1]%p*f[cnt-i]%p)%=p;
cout<<ans<<'\n';
return 0;
}