括號序列
題面
給定一個長度為 \(n\) ,包含 \(*\)、\(?\)、\((\)、\()\) 的字符串。
其中, \(?\) 處字符串缺失,可能是 \(*\)、\((\)、\()\) 中的任意一種。
定義超級括號序列如下:
-
\(()\) 和 \((S)\) 均為符合規范超級括號序列,其中 \(S\) 表示由長度不超過 \(k\) 的 \(*\) 組成。
-
如果 \(A\) 和 \(B\) 為超級括號序列,那么 \(AB\)、\(ASB\) 同樣為符合規范的超級括號序列,其中 \(AB\) 表示把 \(A\) 和 \(B\) 拼在一起形成的字符串。
-
如果 \(A\) 為符合規范的超級括號序列,那么 \((A)\)、\((SA)\)、\((AS)\) 均為符合規范的超級括號序列。
-
所有符合規范的超級括號序列均可以由以上 \(3\) 條規則得到。
求給出的字符串一共能對應多少種符合規范的超級括號序列。
給出 \(n\)、\(k\) 和一個長度為 \(n\) 的字符串,其中 \(1\leq k\leq n\leq 500\) 。
分析
暴力
暴力分 \(15\) 分應該是直接搜索?\(O(3^n)\) ,期望能過前 \(3\) 個點。
區間 \(DP\)
之后發現好像其他暴力不怎么會,注意到 \(n\) 的范圍只有 \(500\) ,我們需要一個 \(O(n^3)\) 的算法來解決這個問題。
考慮一個區間 \(DP\) 。
設 \(dp[l][r]\) 表示區間 \(l\) 到 \(r\) 的答案,之后,模擬上述的規則即可更新。
但之后你會發現你無法通過樣例,思考這樣 \(dp\) 的正確性,發現我們算重了。
對於 \(A\) ,我們也許會把它和后面一個匹配成功的 \(BC\) 合並,但是我們也會將 \(AB\) 合並,並在后面與 \(C\) 合並,考慮給我們的狀態多加一維。
設 \(dp[l][r][0/1]\) 表示當前區間經歷合並的方案數與沒有經歷合並的方案數,只讓沒有經歷合並的方案和在它后面的經歷過合並的方案合並,這樣就能夠避免這種情況。
一些細節
發現實現 \(ASB\) 是最后一個難關,很容易會讓復雜度退化到 \(O(n^4)\) ,考慮設一個新的狀態 \(f[l][r]\) 表示該區間內 \(SB\) 的方案數,之后可以與 \(AB\) 的情況一起轉移。
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e2+10,MOD=1e9+7;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,k;
int g[N][N],f[N][N],dp[N][N][2];
char arr[N];
inline bool check(int l,int r){
if(arr[l]=='('&&arr[r]==')') return true;
if(arr[l]=='('&&arr[r]=='?') return true;
if(arr[l]=='?'&&arr[r]==')') return true;
if(arr[l]=='?'&&arr[r]=='?') return true;
return false;
}
signed main()
{
// freopen("bracket.in","r",stdin);
// freopen("bracket.out","w",stdout);
n=read(),k=read();
cin>>arr+1;
for(int l=1;l<=n;l++){
if(arr[l]!='('&&arr[l]!='?') continue;
int cnt=0;
for(int r=l+1;r<=n;r++){
if(arr[r]==')'||arr[r]=='?') dp[l][r][0]=1;
if(arr[r]!='*'&&arr[r]!='?') break;
cnt++; if(cnt>k) break;
}
}
for(register int len=2;len<=n;len++){ //枚舉區間長度
for(register int l=1;l<=n-len+1;l++){ //枚舉左端點
int r=l+len-1; //右端點
int cnt=0;
for(register int x=l;x<r;x++){
if(arr[x]!='*'&&arr[x]!='?') break;
cnt++; if(cnt>k) break;
f[l][r]=(f[l][r]+(dp[x+1][r][0]+dp[x+1][r][1])%MOD)%MOD;
}
cnt=0;
for(register int x=r;x>l;x--){
if(arr[x]!='*'&&arr[x]!='?') break;
cnt++; if(cnt>k) break;
g[l][r]=(g[l][r]+(dp[l][x-1][0]+dp[l][x-1][1])%MOD)%MOD;
}
for(register int x=l;x<r;x++)
dp[l][r][1]=(dp[l][r][1]+dp[l][x][0]*((f[x+1][r]+(dp[x+1][r][0]+dp[x+1][r][1])%MOD)%MOD)%MOD)%MOD;
if(check(l,r)){ //端點可以為()
dp[l][r][0]=((dp[l][r][0]+dp[l+1][r-1][0])%MOD+dp[l+1][r-1][1])%MOD;
dp[l][r][0]=(dp[l][r][0]+f[l+1][r-1])%MOD;
dp[l][r][0]=(dp[l][r][0]+g[l+1][r-1])%MOD;
}
}
}
printf("%lld\n",(dp[1][n][0]+dp[1][n][1])%MOD);
return 0;
}