学习链接:点击进入
功能
解决多对字符串之间的相互匹配问题
fail指针
fail是失配指针,如果此时匹配失败,那么,我们就要到达这个指针指向的位置继续常数匹配
fail [ i ] 为与以 i 节点为结尾的串的后缀有最大公共长度的前缀的结尾编号,也就是说
fail指针指向的节点所代表的串,是最长的、能与后缀匹配的,且在Trie中出现过的前缀所代表的节点。
代码
const int maxn=1e6+10;
int trie[maxn][26]; //字典树
int sum[maxn]; //记录该单词出现次数
int fail[maxn]; //失败时的回溯指针
int cnt = 0;
void init()
{
memset(trie,0,sizeof(trie));
memset(sum,0,sizeof(sum));
memset(fail,0,sizeof(fail));
cnt=0;
}
void insert(char *str)
{
int p=0,len=strlen(str);
for(int i=0;i<len;i++)
{
int c=str[i]-'a';
if(!trie[p][c])
trie[p][c]=++cnt;
p=trie[p][c];
}
sum[p]++; //当前节点单词数+1
}
void getfail()
{
queue<int>q;
for(int i=0;i<26;i++)//第二层的fail指针提前处理一下
{
if(trie[0][i])
{
fail[trie[0][i]]=0;//指向根节点
q.push(trie[0][i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;i++)
{ //查询26个字母
if(trie[now][i])//存在这个子节点
{
fail[trie[now][i]]=trie[fail[now]][i];
//子节点的fail指针指向当前节点的
//fail指针所指向的节点的相同子节点
q.push(trie[now][i]);
}
else//不存在这个子节点 当前节点的这个子节点指向当前节点fail指针的这个子节点
trie[now][i]=trie[fail[now]][i];
}
}
}
int query(char *str)
{
int p=0,ans=0;
int len=strlen(str);
for(int i=0;i<len;i++)
{ //遍历文本串
p=trie[p][str[i]-'a']; //从s[i]点开始寻找
for(int j=p;j&&sum[j]!=-1;j=fail[j])
{
//一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
ans+=sum[j];
sum[j]=-1; //将遍历国后的节点标记,防止重复计算
}
}
return ans;
}