字符串
trie树和01trie树
这部分内容可以参见之前的blog
字符串哈希
cpp
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
// 使用 unsigned long long 自然溢出通常比手写 mod 快且安全
// 如果怕被卡,可以保留你的大质数 mod
typedef unsigned long long ull;
const int N = 10010;
const ull base = 131;
const ull prime = 233317;
ull a[N];
char s[N];
int n;
// 统一后的 Hash 函数逻辑
ull get_hash(char str[]) {
ull res = 0;
int len = strlen(str);
for (int i = 0; i < len; i++) {
// 按照你习惯的迭代式写法
res = res * base + (ull)str[i] + prime;
}
return res;
}
int main() {
// 保持你 scanf/printf 的简洁性
if (scanf("%d", &n) == EOF) return 0;
for (int i = 1; i <= n; i++) {
scanf("%s", s);
a[i] = get_hash(s);
}
// 排序后去重计数
sort(a + 1, a + n + 1);
int ans = 1;
for (int i = 1; i < n; i++) {
if (a[i] != a[i + 1]) ans++;
}
// 处理 n=0 的特殊情况(虽然题目通常 n>=1)
if (n == 0) printf("0\n");
else printf("%d\n", ans);
return 0;
}
KMP
cpp
#include<iostream>
using namespace std;
const int N = 100010, M = 1000010;
int n, m;
char p[N], s[M];
int ne[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
//next数组的实现
//从二开始 因为next[1] = 0
for(int i = 2, j = 0; i <= n; i++)
{
while(j && p[i] != p[j + 1]) j = ne[j]; //匹配失败回到最大重复的地方
if(p[i] == p[j + 1]) j++;
ne[i] = j; //将当前子字符串p[0到i]的最长重复前后缀长度赋给ne[i]
}
//KMP匹配过程
for(int i = 1, j = 0; i <= m; i++)
{
while(j && s[i] != p[j + 1]) j = ne[j];
//如果不匹配就移动最小距离到最长重复前后缀的地方重新匹配
//或者j==0时字符串s与模式串p开头就不一样 s就向后走
if(s[i] == p[j + 1]) j++;
if(j == n)
{
cout << i - n << " ";
j = ne[j]; //匹配成功后返回到最小移动 也就是最大重复的前后缀的地方再检验是否还有匹配的
}
}
return 0;
}
Manacher算法
cpp
//模板
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1.2e7+10;
int p[2*N];
char s1[2*N];
int main(){
string s;
cin>>s;
for (int i=0;i<s.length();i++){
s1[2*i+1]=s[i];
s1[2*i]='#';
}
s1[2*s.length()]='#';
s=s1;
int c=0;
int r=0;
int maxlen=1;
for (int i=0;i<s.length();i++){
p[i]=i<r?min(p[2*c-i],r-i):1;
while (i-p[i]>=0&&s[i-p[i]]==s[i+p[i]])
++p[i];
if (i+p[i]>r){
c=i;
r=i+p[i];
}
maxlen=max(maxlen,2*p[i]-1);
}
cout<<maxlen/2<<endl;
return 0;
}