1. 程式人生 > 實用技巧 >[實驗艙 CSP/NOIP新賽制內部挑戰賽5] B.小F的編碼(BFS/DP)

[實驗艙 CSP/NOIP新賽制內部挑戰賽5] B.小F的編碼(BFS/DP)

Problem

題目地址

Solution

  • 考慮一個簡化版問題:給定長度為 \(m\) 的字串 \(S\),和一些模式串 \(c_i\),問有多少種方案拼成 \(S\)

  • 思路:\(f[i]\) 表示有多少種方案拼成 \(S[1...i]\),那麼有轉移 \(f[i]=\sum_{c_x=S[k...i]} f[k]\),答案就是 \(f[m]\)具體的,看作有 \(m+1\) 個點,若 \(c_x = S[k...i]\),則在 \(k->i\) 連一條邊,最統計有多少種從 \(0\)\(m\) 的路徑。

題解

\(f[i,j]\) 表示第 \(i\) 個字串匹配了 \(j\)

位是否可達。

同樣把 \((i,j)\) 這個狀態看成一個點。\((i,j)->(u,v)\) 之間有邊則說明可以將 \(c_u\) 接上去,即滿足:\(S_i[j...len]=S_u[1...v]\)

注意處理一些細節。初始狀態 \(f[i,0]=1\),最終看是否有 \(f[i,n]=1\)。狀態數 \(O(nL)\),建邊複雜度 \(O(n^2L^2)\),時間複雜度 \(O(n^2L^2)\)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
typedef pair<int,int> PII;
const int N = 57, maxn = N*N;
int n,cnt;
int head[maxn];
int f[N][N];
char str[N][N];
struct Edge {
	int next,to;
}edge[maxn*maxn];
inline void add(int u,int v) {
	edge[++cnt] = (Edge)<%head[u],v%>;
	head[u] = cnt;
}
bool check(int x,int lx,int y,int ly,int len) {
	for(int i=0;i<len;++i)
		if(str[x][lx+i] ^ str[y][ly+i]) return false;
	return true;
}
int Getid(PII x) {
	return x.fi*N + x.se;
}
PII Getpii(int x) {
	return mp(x/N, x%N);
}
void work() {
	memset(str, 0, sizeof(str));
	memset(head, 0, sizeof(head)); cnt = 0;
	memset(f, 0, sizeof(f));
	n = read();
	for(int i=1;i<=n;++i) scanf("%s",str[i]+1);
	for(int i=1;i<=n;++i) {
		int len = strlen(str[i]+1);
		for(int j=0;j<=len;++j) {
			for(int u=1;u<=n;++u) {
				if(j==0 && i==u) continue;
				int ulen = strlen(str[u]+1);
				if(ulen >= len-j) {
					if(check(i,j+1,u,1,len-j)) {
						add(Getid(mp(i,j)), Getid(mp(u,len-j)));
					}
				} else {
					if(check(i,j+1,u,1,ulen)) {
						add(Getid(mp(i,j)), Getid(mp(i,j+ulen)));
					}
				}
			}
		}
	}
	queue<int> q;
	for(int i=1;i<=n;++i) {
		int len = strlen(str[i]+1);
		q.push(Getid(mp(i,0)));
		f[i][0] = 1;
	}
	while(!q.empty()) {
		int u = q.front(); q.pop();
		PII uu = Getpii(u); int x = uu.fi, y = uu.se;
		for(int i=head[u];i;i=edge[i].next) {
			int v = edge[i].to;
			PII vv = Getpii(v); int nx = vv.fi, ny = vv.se;
			if(!f[nx][ny]) {
				f[nx][ny] = f[x][y]; q.push(v);
			}
		}
	}
	bool flag = 0;
	for(int i=1;i<=n;++i) flag |= f[i][strlen(str[i]+1)];
	if(flag) puts("No");
	else puts("Yes");
}
int main()
{
	int T = read();
	while(T--) work();
	return 0;
}
/*
1
2
00
000

No
*/

Summary

可能有一點相似題:[AtCoder Regular Contest 084] D - Small Multiple