(AtCoder Beginner Contest 287)(D 字符串前后缀合并匹配)(E Trie求最长公共前缀(LCP)) (F 树形背包dp)

D - Match or Not(字符串前后缀合并匹配)

题目大意:

给定两个字符串S和T,对于每个x = 0,1,2...|T|求S的子串(前x个字符和后|T|-x个字符在不改变顺序的情况下组成)是否与T相同
(|T|表示字符串T的长度)


解题思路:

只有当s[i] == '?' ||t[i] == '?' || s[i] == t[i]时两个字符串才匹配,所以我们可以对S和T的前后缀进行匹配,如果同时满足pre[i]和suf(|T|-i)才能说明S的子串与T相同(O(1))


代码实现:

#include<bits/stdc++.h>
using namespace std;
# define int long long
# define endl "\n"
const int N = 2e5 + 10, inf = 1e18;

void solve() {

	string s;
	string t;
	cin >> s >> t;
	vector<int> pre(s.size()+1,false),suf(s.size()+1,false);
	pre[0] = true;
	auto cal = [&](char a,char b)->bool{
		return (a=='?'||b=='?'||a==b);	
	};
	
	for(int i = 0;i < t.size();++i){
		if(!cal(s[i],t[i])) break;
		pre[i+1] = true;	
	}
	
	reverse(s.begin(),s.end());
	reverse(t.begin(),t.end());
	
	suf[0] = true;
	for(int i = 0;i < t.size();++i){
		if(!cal(s[i],t[i])) break;
		suf[i+1] = true;
	}
	
	for(int i = 0;i <= t.size();++i){
		if(pre[i]&&suf[t.size()-i]){
			cout<<"Yes"<<endl;
		}
		else cout<<"No"<<endl;
	}
}
int tt;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	tt = 1;
//	cin >> tt;
	while (tt--) solve();


	return 0;
}

E - Karuta(Trie求最长公共前缀(LCP))

题目大意:

给定N个字符串,求N个字符串对除了自己以外的字符串的最长公共前缀长度(LCP)


解题思路:

Trie树的模板题了,但是因为要求除了自己以外的字符串的LCP,所以我们需要对Trie树的每个节点进行计数,如果同一个节点超过了2,说明该节点除了本字符串至少有1个其他字符串也有该节点,可以对答案进行计数,否则该节点只有当前字符串有,结束计数。


代码实现:

#include<bits/stdc++.h>
using namespace std;
# define int long long
# define endl "\n"
const int N = 5e5 + 10, inf = 1e18, mod = 1e9 + 7;
struct Trie{
	int n;
	int idx;
	vector<vector<int>> son;
	vector<int> cnt;
	Trie(int n_):n(n_){
		son.resize(n+1);
		cnt.resize(n+1);//树的大小
		for(int i = 0;i <= n+1;++i){
			son[i].resize(28);//字符集大小
		}
		idx = 0;
	}
	
	void insert(string s){
		int p = 0;
		for(int i = 0;i < s.size();++i){
			int u = s[i]-'a';
			if(!son[p][u]) son[p][u] = ++idx;
			p = son[p][u];
			cnt[p]++;
		}
	}
	
	int query(string t){
		int p = 0;
		int sum = 0;
		for(int i = 0;i < t.size();++i){
			int u = t[i]-'a';
			p = son[p][u];
			if(cnt[p]<=1) break;
			sum++;
		}
		return sum;
	}
	
};
string a[N];
void solve() {
	int n;
	cin>>n;
	Trie tr(N);
	for(int i = 1;i <= n;++i)
	{
		cin>>a[i];
		tr.insert(a[i]);
	}
	
	for(int i = 1;i <= n;++i){
		cout<<tr.query(a[i])<<endl;
	}

}
int tt;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	tt = 1;
//	cin >> tt;
	while (tt--) solve();


	return 0;
}

F - Components(树形背包dp)

题目大意:

给定一个N个节点的树,求该树的导出子图满足有X = 1,2,....N个联通分量的情况


解题思路:

树形背包dp: f[i][j][0/1]表示以i为为根的子树中选j个联通分量,根节点(0/1)选或者不选的情况

考虑到如果相邻两个节点如果同时选,那么对于联通分量的数量不做贡献,如果对于不相邻的节点同时选择会增加联通分量的数量
所以转移如下:
定义g[j][0/1] 表示以u为根的子树中选j个联通分量,其中根节点选/不选

f[u][j][0] = g[j-k][0]*(f[v][k][0]+f[v][k][1]);
f[u][j][1] = g[j-k][1]*f[v][k][0]+g[j-k][1]*f[v][k+1][1]

  • 当根节点u不选时,子节点v选不选都不会影响联通分量的数量
  • 当根节点选时,如果子节点不选那么也不影响联通分量的数量,但是如果子节点也选的话联通分量的数量会减一,所以需要在子节点的树中多选一个联通分量

初始化时对于每个节点f[u][0][0] = f[u][1][1] = 1


代码实现:

#include<bits/stdc++.h>
using namespace std;
# define int long long
# define endl "\n"
const int N = 5100 + 10, inf = 1e18, mod = 998244353;
vector<int> e[N];
int f[N][N][2];//以i为根的子树中选j个联通分量且节点i(0/1)选不选 
int sz[N];
int g[N][2];
void dfs(int u,int fa){
	sz[u] = 1;
	f[u][0][0] = f[u][1][1] = 1;
	for(auto v:e[u]){
		if(v  == fa) continue;
		dfs(v,u);
		for(int j = 0;j <= sz[u]+sz[v];++j){
			g[j][0] = f[u][j][0];
			g[j][1] = f[u][j][1];
			f[u][j][0] = f[u][j][1] = 0;
		}
		for(int j = sz[u]+sz[v];j>=0;--j){
			for(int k = max(0ll,j-sz[u]);k<=min(j,sz[v]);++k){
				f[u][j][0] += g[j-k][0]*(f[v][k][0]+f[v][k][1])%mod;
				f[u][j][1] += (g[j-k][1]*f[v][k][0])%mod+(g[j-k][1]*f[v][k+1][1])%mod;
				(f[u][j][0]+=mod)%=mod;
				(f[u][j][1]+=mod)%=mod;
			}
		}
		sz[u] += sz[v];
	}
}


void solve() {
	int n;
	cin>>n;
	for(int i = 1;i < n;++i){
		int a,b;
		cin>>a>>b;
		e[a].push_back(b);
		e[b].push_back(a);
	}
	
	dfs(1,0);
	for(int i = 1;i <= n;++i){
		cout<<(f[1][i][0]+f[1][i][1]+mod)%mod<<endl;
	}
}
int tt;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	tt = 1;
//	cin >> tt;
	while (tt--) solve();


	return 0;
}

posted @ 2023-01-29 19:48  empty_y  阅读(86)  评论(0)    收藏  举报