朴素字符串查找
例如:给n个字符串,再给一个字符串t,如何快速查找t是否在s中出现过?
朴素的方法:循环遍历s1~sn,并比较si是否与t相等。
字典树
也叫Trie树,是一种树形结构,节点可以存储(当然也可以不存储)一些变量用于表示该字符串的数量。节点上的数字表示节点编号,每条边表示一个字符。
每个节点仅存在一条到根节点的路径,路径的所有边表示一条字符串。
//创建
int nex[N][27];
int cnt[N];//cnt[i]表示以结点i结尾的字符串数量
int idx=2;
//字符串加入
void insert(char s[])
{
int n=strlen(s+1);
int x=1;//开始位置
for(int i=1;i<=n;++i)
{
//检查x是否在s[i]边上
if(!nex[x][s[i]-'a'])
nex[x][s[i]-'a']=idx++; //idx是一个新的点
x=nex[x][s[i]-'a'];//移动到新的点 =往下走
}
cnt[x]++;
}
//判断某个字符串在trie中的出现次数
int check(char s[])
{
int n=strlen(s+1);
int x=1;
for(int i=1;i<=n;++i)
x=nex[x][s[i]-'a'];
return cnt[x];
}
例题:
Manacher算法
回文串的性质
类似AABBAA对于每个i具有s[i]=s[n+1-i]的字符串。
回文半径:对于一个回文中心i,如果它的半径为r,如果是奇数长度的回文串的中心,说明[i-r+1,i+r-1]为一个回文串。如果i是偶数回文中心,说明回文半径没有意义(Manachar算法会解决这个问题)。
回文的递归性质:某个点的回文半径相比于某个回文中心的点的回文半径一定相等或更大。
Manachar算法
前面说过偶数长度的回文的中心没有意义,如:ABBA中的B就不存在回文半径。
因此Manachar算法就是将所有的回文串都转化为奇数长度的回文串。
方法:在字符串的中间和头尾插入特殊字符,如:ABBA,转化成^#A#B#B#A#$,,因此我们得到第三个#的回文半径为5。
Manachar算法是一种O(n)复杂度计算字符串中每个位置作为回文中心的回文半径的算法。位置i的回文半径以p[i]表示,意思是在转换后的字符串中[i-p[i]+1,i+p[i]-1]是回文的。
算法主体:
int C = 0, R = 0;//初始都从0开始
for (int i = 1; i <= 2 * n + 1; ++i)
{
p[i] = i < R ? min(R - 1, p[2 * C - i]) : 1;
//如果p[i]可以更大的话,就持续更新
while (s[i + p[i]] == s[i - p[i]])
p[i]++;
//如果此时i可以作为一个更好的C,就替换
if (i + p[i] > R)
C = i, R = i + p[i];
}
模板例题: