• 题意:[CF 1015F](https://codeforces.com/contest/1015/problem/F)
    给你一个模式串A(一个不一定合法的括号序列),让你构造长度为2*n的合法括号序列,问有多少种方案使得含A为其子串。(n<=100)
  • 思路:
    KMP+DP
    \(dp[i][j][k][0/1]\)表示当前匹配到了\(a[i]\),\(s[j]\),此时左括号数-右括号数为\(k\),是否含有A的方案数。
    然后j->j+1,枚举j+1是'('还是')',然后将其按照KMP匹配a。方程见代码。
  • code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=205;
const ll mod=1e9+7;
char s[N];
ll dp[N][N][N][2];
int n,len,a[N],nxt[N],to[N][3];
void init() {
	int j=0;
	for(int i=2;i<=len;i++) {
		while(j&&a[i]!=a[j+1])j=nxt[j];
		if(a[i]==a[j+1])j++;
		nxt[i]=j;
	} 
}
int Fld(int i,int c) {
	if(to[i][c+1]!=-1)return to[i][c+1];
	while(i&&a[i+1]!=c)i=nxt[i];
	return to[i][c+1]=i+(a[i+1]==c);
}

int main() {
	memset(to,-1,sizeof(to));
	scanf("%d",&n);
	scanf("%s",s+1);len=strlen(s+1);
	for(int i=1;i<=len;i++)a[i]=(s[i]=='(')?1:-1;
	init();
	dp[0][0][0][0]=1;
	for(int j=0;j<(n<<1);j++) {
		for(int i=0;i<=len;i++) {
			for(int k=0;k<=n;k++) {
				for(int f=0;f<=1;f++) {
					if(!dp[i][j][k][f])continue;  //优化
					if(k) {int t=Fld(i,-1);dp[t][j+1][k-1][f|(t==len)]=(dp[t][j+1][k-1][f|(t==len)]+dp[i][j][k][f])%mod;}
					if(k<n) {int t=Fld(i,1);dp[t][j+1][k+1][f|(t==len)]=(dp[t][j+1][k+1][f|(t==len)]+dp[i][j][k][f])%mod;}
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<=len;i++) ans=(ans+dp[i][n<<1][0][1])%mod; 
	printf("%lld",ans);
	return 0;
} 
//5
//()))()

ps.大常数选手的一些不超时小优化
1.(必须加):if(!dp[i][j][k][f])continue;因为有很多不可能的状态,大量优化时间。
2.记忆化Fld
效果从下到上分别为:1+2,0,1