字符串
1、Trie
`
const int N = 100010;
int idx; // 各个节点的编号,根节点编号为0
int son[N][26];//Trie 树本身
int cnt[N];//以编号为 x 为结尾的字符串的个数
int n;
void insert(string s){
int p = 0;//指向根节点,想象每个节点上有26个字母的坑位,实际上还是一个个节点之间建边
for(int i = 0; i < s.size(); i++){
int u = s[i] - 'a';
//如果树的该节点上没有当前字符,用idx为当前字符创建新的节点,保存该字符
if(!son[p][u]){
son[p][u] = ++idx;
}
p = son[p][u];//更新指向的节点,像链表一样
}
//这个时候,p 等于字符串 s 的尾字符所对应的 idx,每个这个idx对应的都是独一无二的一个字符串,即使尾字符相同,idx也会不同
//cnt[p] 保存的是字符串 s 出现的次数
cnt[p] ++;
}
int query(string s){
int p = 0;//指向根节点
for(int i = 0; i < s.size(); i++){
int u = s[i] - 'a';
//如果走不通了,即树中没有保存当前字符
if(!son[p][u]){
return 0;}
p = son[p][u];//否则指向下一个节点
}
//循环结束的时候,p 等于字符串 s 的尾字符所对应的 idx
return cnt[p];
}
int main(){
cin >> n;string s;char q;
while(n--){
cin >> q >> s;
if(q == 'I'){
insert(s);
}
else{
cout << query(s) << endl;
}
}
}`
2、KMP(给定主串 S 和模式串 P 。主用于模式串仅有一个的情况)
`
const int NUM = 1000010;
int ne[NUM];//其中存放的是模式串的下标,假设ne[i]等于5,就意味着模式串的指针j将会移到5的地方,5序号所在处本身是已匹配过的相同的char,因此移动后所需判断的是j+1处是否相同
int main() {
string s1, s2; cin >> s1 >> s2;//s2是模式串(通常较短)
ne[0] = -1;
//以下j是前缀的指针,i是后缀的指针
for (int i = 1, j = -1; i < s2.size(); i++) {
while (j >= 0 && s2[i] != s2[j + 1]) {
j = ne[j];//例如aba-cf-aba-df,遇到c和d不相等时,j(前缀)就回溯到第一个a,此时还可以保证这个a和d前面的a相同
}
if (s2[i] == s2[j + 1]) { j++; }相等时,j移到相等的位置
ne[i] = j;
}
for (int i = 0, j = -1; i < s1.size(); i++) {
while (j != -1 && s1[i] != s2[j + 1]) {
j = ne[j];
}
if (s1[i] == s2[j + 1]) {
j++;
}
/while ((j<<1) > (i+1)) {
j = neb[j];//最大不重叠的相同前后缀
}/
if (j == s2.size() - 1) {
cout << i - j + 1 << endl;//如果模式串完全匹配,输出其首位位置
j = ne[j];
}
}
for (int i = 0; i < s2.size(); i++) {
cout << ne[i] + 1 << " ";//输出模式串所有前缀下标
}
return 0;
}
`
3、AC自动机(给定主串 S 和多个模式串 P1, P2, …, Pm。在一次扫描 S 的过程中找到所有模式串出现的位置。KMP的升级版)

浙公网安备 33010602011771号