//本题是一道字典树的模板题
//字典树是一种高效率存储多个字符串的数据结构
//其每个结点的权值代表以该结点结尾的字符串的数量,每条边存储一个字符
//从根结点开始,按某一路径遍历到某一结点,即得到一种字符串,其个数等于当前结点存储的数值
//如从根结点开始,依次走过'a''b''c'三条边到达9号结点,9号结点保存的数字是3
//则得到字符串"abc",其数量为3个
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+100;
int nex[N][27];//nex[i][0]表示从结点i出发,边为'a'的下一个结点地址(假设字符串全由小写字母构成)
//如1号结点与2号结点间存在一条记录字母'a'的边,则nex[1]['a'-'a']=2
//如8号结点与9号结点间存在一条记录字母'c'的边,则nex[8]['c'-'a']=9
int cnt[N];//cnt[i]表示以结点i结尾的字符串的数量,即每个结点的权值
int idx=2;//用于动态开点,初始时只有一个根结点1
void insert(char *S)//在字典树中插入字符串S的信息
{
int x=1;//x表示结点编号,初始从根结点(1号)开始
for(int i=0;S[i]!='\0';i++)//遍历字符串S
{
//先检查x是否存在S[i]的边
if(nex[x][S[i]-'a']==0)//从结点x出发,目前还没有记录当前字母的边
{
nex[x][S[i]-'a']=idx++;//则新建一个边记录之,同时动态开点
}
x=nex[x][S[i]-'a'];//到达下一个结点编号
}
//cnt[x]++;
//最终x到达字符串末尾字符对应的结点上,其计数值加1
}
bool check(char *T)//在字典树中查找字符串T(计算出现的次数)
{
int x=1;//x表示结点编号,初始从根结点(1号)开始
for(int i=0;T[i]!='\0';i++)//遍历字符串T
{
x=nex[x][T[i]-'a'];//根据当前字符,x不断向下追溯,最终到达结尾
//若不存在这个字符(记录这个字符的边),则x=0,后续x将一直为0
}
//return cnt[x];//返回字符串T出现的次数,即结尾字符对应的结点所记录的权值
return x;//本题返回x即可,只需判断x是否为0
}
int main()
{
int n,m;
cin>>n>>m;
while(n--)//N个字符串
{
char S[N];
cin>>S;
insert(S);//每输入一个字符串,就将其信息插入字典树
}
while(m--)//M个询问
{
char T[N];
cin>>T;
if(check(T))cout<<"Y"<<endl;//在字典树中找到T,输出Y
else cout<<"N"<<endl;//没找到,输出N
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+9;
int nex[N][26];
int cnt[N];
int idx=2;
void insert(char s[]){
int x=1;
for(int i=0;s[i]!='\0';i++){
if(!nex[x][s[i]-'a'])nex[x][s[i]-'a']=idx++;
x=nex[x][s[i]-'a'];
}
}
bool check(char *T){
int x=1;
for(int i=0;T[i]!='\0';i++){
x=nex[x][T[i]-'a'];
if(x==0)return 0;
}
return 1;
}
int main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
char s[N];cin>>s;
insert(s);
}
for(int i=1;i<=m;i++){
char s[N];cin>>s;
cout<<(check(s)?"Y":"N")<<"\n";
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+6;
int nex[N][27];//nex[i][a]=j表示从结点i到结点j嗲表的是字母a
int cnt[N];
int idx=2;
void insert(char s[]){
int x=1;//根结点
for(int i=0;s[i]!='\0';i++){
cnt[x]++;
if(!nex[x][s[i]-'a'])nex[x][s[i]-'a']=idx++;
x=nex[x][s[i]-'a'];
}
cnt[x]++;
}
int check(char t[]){
int x=1;
for(int i=0;t[i]!='\0';i++){
x=nex[x][t[i]-'a'];
}
return cnt[x];
}
int main(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
char s[N];cin>>s;
insert(s);
}
for(int i=1;i<=m;i++){
char t[N];cin>>t;
cout<<check(t)<<'\n';
}
return 0;
}
//本题是一道比较简单的字典树的应用题
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int nex[maxn][27];//nex[x][0]表示从第x个结点开始,边为'a'的下一个结点的地址(全为小写字母)
int cnt[maxn];//cnt[i]存储以第i个结点结尾的前缀的数量
int idx=2;//用于动态开点
void Insert(string s)//将字符串s插入字典树中
{
int x=1;//初始从根结点(1号)开始
for(int i=0;i<s.size();i++)//遍历字符串
{
cnt[x]++;//以当前字母结尾的前缀数量+1
if(nex[x][s[i]-'a']==0)//若该字母未记录过(不存在对应的边)
{
nex[x][s[i]-'a']=idx++;//则动态开点并记录之
}
x=nex[x][s[i]-'a'];//继续向下追溯
}
cnt[x]++;//以最后一个字符结尾的前缀的数量+1
}
int Search(string s)//在字典树中查找字符串s,并返回匹配成功的次数
{
int x=1;//初始从根结点(1号)开始
for(int i=0;i<s.size();i++)//遍历字符串s
{
x=nex[x][s[i]-'a']; //不断向下追溯直到结尾
}
return cnt[x];//返回结尾结点记录的前缀数量
}
int main()
{
int N,M;
cin>>N>>M;
while(N--)//输入N组字符串并插入字典树
{
string s;
cin>>s;
Insert(s);
}
while(M--)//M轮询问
{
string s;
cin>>s;
cout<<Search(s)<<endl;//在字典树中查找匹配成功的次数并输出
}
return 0;
}
//本题考察字典树的扩展应用
//其具体算法仍是字典树的插入与查询
//需要注意的是当前字符串不能与自己匹配,
//由于插入与删除的本质相同,只是cnt数组对应位置的增加或减小,故只需改写插入函数即可
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
string str[maxn];//存储原始字符串组
int nex[maxn][27];//nex[x][0]表示从第x个结点出发,边为'a'的下一个结点地址
int cnt[maxn];//cnt[i]表示以第i个结点结尾的前缀的数量
int idx=2;//用于动态开点
void Insert(string s,int tag)//将字符串s插入字典树中,或将其从字典树中删除
//若传入tag=1,则为插入;若传入tag=-1,则为删除
//插入与删除的本质是令对应的cnt[x]+1或-1
{
int x=1;//初始从根结点(1号)开始
for(int i=0;i<s.size();i++)//遍历字符串s
{
cnt[x]+=tag;//对每个字符,以该字符结尾的前缀数量均+1/-1
if(nex[x][s[i]-'a']==0)//若该字符(存储该字符的边)未被记录
{
nex[x][s[i]-'a']=idx++;//则动态开点并记录之
}
x=nex[x][s[i]-'a'];//继续向下追溯
}
cnt[x]+=tag;//结尾字符对应的前缀数量+1/-1
}
int Search(string s)//在字典树中查找与s最接近的字符串,并返回匹配的最长前缀的长度
{
int x=1;//初始从根结点(1号)开始
int ans=0;//记录匹配的最长前缀的长度
for(int i=0;i<s.size();i++)//遍历字符串
{
if(nex[x][s[i]-'a']==0)//已经无法再匹配(不存在记录当前字符的边)
{
return ans;//返回之前累计的长度
}
x=nex[x][s[i]-'a'];//若能继续匹配,则继续向下追溯
if(cnt[x]==0)return ans;//已经不存在以x结点结尾的前缀,返回之前累计的长度
//注意以上这句不可省略,因为在删除操作中只是减少了字符串出现的次数,并没有删除之前记录的字符
ans++;//计数值加1,重复上述操作
}
return ans;//最终返回ans
}
int main()
{
int N;
cin>>N;
for(int i=0;i<N;i++)//输入N个字符串
{
cin>>str[i];
Insert(str[i],1);//插入
}
for(int i=0;i<N;i++)//N组查询
{
Insert(str[i],-1);//先将当前字符串删除
cout<<Search(str[i])<<'\n';//查询匹配的最长前缀的长度并输出
Insert(str[i],1);//将当前字符串重新插入以恢复字典树
}
return 0;
}