给定两个字符串S和T,对于每个x = 0,1,2...|T|求S的子串(前x个字符和后|T|-x个字符在不改变顺序的情况下组成)是否与T相同 只有当s[i] == '?' ||t[i] == '?' || s[i] == t[i]时两个字符串才匹配,所以我们可以对S和T的前后缀进行匹配,如果同时满足pre[i]和suf(|T|-i)才能说明S的子串与T相同(O(1)) 给定N个字符串,求N个字符串对除了自己以外的字符串的最长公共前缀长度(LCP) Trie树的模板题了,但是因为要求除了自己以外的字符串的LCP,所以我们需要对Trie树的每个节点进行计数,如果同一个节点超过了2,说明该节点除了本字符串至少有1个其他字符串也有该节点,可以对答案进行计数,否则该节点只有当前字符串有,结束计数。 给定一个N个节点的树,求该树的导出子图满足有X = 1,2,....N个联通分量的情况 树形背包dp: f[i][j][0/1]表示以i为为根的子树中选j个联通分量,根节点(0/1)选或者不选的情况 考虑到如果相邻两个节点如果同时选,那么对于联通分量的数量不做贡献,如果对于不相邻的节点同时选择会增加联通分量的数量 f[u][j][0] = g[j-k][0]*(f[v][k][0]+f[v][k][1]); 初始化时对于每个节点f[u][0][0] = f[u][1][1] = 1D - Match or Not(字符串前后缀合并匹配)
题目大意:
(|T|表示字符串T的长度)
解题思路:
代码实现:
#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))
题目大意:
解题思路:
代码实现:
#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)
题目大意:
解题思路:
所以转移如下:
定义g[j][0/1] 表示以u为根的子树中选j个联通分量,其中根节点选/不选
f[u][j][1] = g[j-k][1]*f[v][k][0]+g[j-k][1]*f[v][k+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;
}