CF 106 div 2 (乘法dp/後綴自動機)

比賽連接

D

簡單區間dp

一個合法的括號序列染色,一對匹配的括號必須有一個被染成紅色或者藍色,另一個不染色。

被染色的相鄰括號不能相同顏色,問有幾種染色的方法。

直接區間dp,轉移就是考慮每種情況然後相乘就可以了。

#include<bits/stdc++.h>
#include<stack> 
#define MOD  1000000007
using namespace std;

char s[1000];
int p[1000];
long long dp[3][3][705][705];
int main(){
	cin>>s+1;
	int len=strlen(s+1);
	stack<int>stk;
	for (int i=1;i<=len;i++){
		if (s[i]=='(')stk.push(i);
		else {
			p[i]=stk.top();
			p[stk.top()]=i;
			stk.pop();
		}
	}
	memset(dp,0,sizeof(dp));
	
	for (int i=2;i<=len;i++){  //changdu
		for (int l=1;l<=len-i+1;l++){  //weizhi
			int m=p[l];
			int r=l+i-1;
			if (m>l&&m!=r){ //中間截斷 
				for (int j=0;j<3;j++){
					for (int k=0;k<3;k++){
						for (int h=0;h<3;h++){
							for (int g=0;g<3;g++){
								if (h&&g&&g==h) continue;
								dp[j][k][l][r]+=(dp[j][h][l][m]*dp[g][k][m+1][r]);
								dp[j][k][l][r]%=MOD;
							}
						}
					}
				}
			}
			else if (m==r){
				if (i==2){
					dp[0][1][l][r]=dp[0][2][l][r]=dp[1][0][l][r]=dp[2][0][l][r]=1;
				}
				else{
					for (int j=0;j<3;j++){
						for (int k=0;k<3;k++){
							if (j==0&&k==0||j&&k) continue;
							for (int h=0;h<3;h++){
								if (j&&h&&j==h) continue;
								for (int g=0;g<3;g++){
									if (k&&g&&k==g) continue;
									dp[j][k][l][r]+=(dp[h][g][l+1][r-1]);
									dp[j][k][l][r]%=MOD;
								}
							}
						}
					}	
				}
			}
		}
	}
	long long ans=0;
	for (int i=0;i<3;i++){
		for (int j=0;j<3;j++){
			ans+=dp[i][j][1][len];
			ans%=MOD;
		}
	}
	cout<<ans<<endl;
}

E

後綴自動機

其實還是不太會這個東西,又看了一遍clj老師的講稿,但是這題的應用確實還蠻基礎的。

正反建兩個後綴自動機,然後對於每個字符串匹配最優的情況就可以了。

#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#define N 100005 
#define INF 0x3f3f3f3f
using namespace std;
char s[N];
char s1[1005];

int n;
char text[N];
struct state{
	state *par, *go[26];
	int val,fst;  //val==maxlen  fst==very left
	bool f,vis; //is the added node
	state(int _val) :
		par(0), val(_val), fst(INF), f(0), vis(0){
		memset(go,0,sizeof(go));
	}
};

struct suffixautomaton{
	state *root, *last;
	void extend(int w){
		state *p=last;
		state *np=new state(p->val+1);
		while (p&&p->go[w]==0){
			p->go[w]=np;
			p=p->par;
		}
		if (p==0) np->par=root;
		else{
			state *q=p->go[w];
			if (p->val+1==q->val){
				np->par=q; 
			}
			else{
				state *nq= new state(p->val+1);
				memcpy(nq->go,q->go,sizeof(q->go));
				nq->par=q->par;
				q->par=nq;
				np->par=nq;
				while (p&&p->go[w]==q) {
					p->go[w]=nq;
					p=p->par;
				}
			}
		}
		last=np;
		np->f=true;
	}
	void work(state* now, int dep){
		if (now->vis) return;
		for (int i=0;i<26;i++){
			if (now->go[i]){
				text[dep+1]='A'+i;
				work(now->go[i],dep+1);
				now->fst=min(now->fst, now->go[i]->fst -1);
				
			}
		}
		if (now->f)  now->fst=min(now->fst, now->val);
		now->vis=true;
		return;
	}
	void init(char *s){
		root=last=new state(0);
		int len=strlen(s+1);
		for (int i=1;i<=len;i++){
			extend(s[i]-'A');
		}
		//for (int i=0;i<26;i++) cout<<i<<" "<<root->go[i]<<endl;
		work(root,0);
	}
	void match(char *s, int *res){
		int len=strlen(s+1);
		state* now=root;
		for (int i=1;i<=len;i++){
			if (now->go[ s[i]-'A']){
				now=now->go[ s[i]-'A'];
				res[i]=now->fst;
			}
			else{
				for (int j=i;j<=len;j++) res[j]=INF;
				break;
			}
		}
	}
}A,B;
int a[1005],b[1005];

int main(){
	scanf("%s",s+1);
	A.init(s);
	int len=strlen(s+1);
	reverse(s+1,s+len+1);
	B.init(s);
	scanf("%d",&n);
	int ans=0;
	for (int i=1;i<=n;i++){
		scanf("%s",s1+1);
		int len1=strlen(s1+1);
		if (len1==1) continue;
		A.match(s1,a);
		/*
		for (int i=1;i<=len1;i++){
			cout<<a[i]<<" ";
		} 
		cout<<endl;
		*/
		reverse(s1+1,s1+len1+1);
		B.match(s1,b);
		/*
		for (int i=1;i<=len1;i++){
			cout<<b[i]<<" ";
		}
		cout<<endl;
		*/
		for (int i=1;i<=len1;i++){
			if (a[i]+b[len1-i]<=len){
				ans++;
				break;
			}
		}
	}
	printf("%d",ans);
	return 0;
} 

字符串

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章