集训刚开始几天学了一下字符串算法(只看了一点点), 不过今天开始要看一些数学计算几何方面的东西, 所以先写个总结, 以后回来继续看.
暂时给出kmp以及一些练习题, AC自动机和hash后续给出
主要介绍:
0. Trie树
1. kmp算法
2. AC自动机 (待填坑)
3. 字符串哈希 (待填坑)
Trie树
又叫字典树, 其实这个数据结构还是比较直观的, 比如所有字符串都是26个字母的, 那么这个字典树就是一个26叉树, 如果abcd存在, 那么字典树里就有root -> a -> b -> c -> d的一条路.
#include <cstdio>
#include <cstring>
using namespace std;
const int segma_size = 26;
const int maxsize = 50000;
struct TrieNode{
int val;
int cnt;
int next[segma_size];
TrieNode(){
memset(next, 0, sizeof(next));
cnt=0;
}
};
TrieNode trie[maxsize];
int tcnt;
void init()
{
tcnt = 1;
}
void insert(char *key)
{
char *p = key;
int u = 0;
while(*p)
{
if(trie[u].next[*p-'a'] == 0)
trie[u].next[*p-'a'] = tcnt++;
u = trie[u].next[*p-'a'];
p++;
}
trie[u].cnt = 1;
}
int find(char *key)
{
char *p = key;
int u = 0;
while(*p)
{
if(trie[u].next[*p-'a'] == 0)
return 0;
u = trie[u].next[*p-'a'];
p++;
}
return trie[u].cnt;
}
这是我写的一个静态的Trie树, 不要用new, delete来做..太慢
kmp算法
kmp算法是用于处理两个字符串匹配的算法, 有一个较长的文本串S(长度n
), 模式串P(长度m
), 在S中查找P是否出现过, 暴力算法为O(n*m)
.
KMP算法是一个比较晦涩的算法, 但是可以做到O(n+m)
的复杂度
大意是计算出模式串的Next
数组.在暴力匹配的时候, i指针在文本串上向后滑动, j指针在模式串上向后滑动, 如果匹配失败, 则i回滚到模式串的开头后一个位置继续开始匹配, 即 每次与模式串匹配失败, i向前回滚, 因此最坏情况下, 每次都回滚m个位子, 总共n次, 复杂度为O(m*n)
而求出Next数组后, i指针不前移, 利用Next数组将模式串跳转,
例如模式串是abcdabce
, 匹配abcdabc
都成功, 然而匹配e的时候发现, 跟文本串(例如abcdabcfabc)的f不一样, 此时, abcdabce中, e前面的abc和文本串f前面的abc相同, 而e前面的abc和模式串开头的abc也相同, 那么只需要从d开始与文本串匹配即可. (前面已经一样, 不用比较了)
给一个非常详细的帖子, 从头到尾理解KMP
或者可以看看hihocoder里的KMP算法那个, 也不错
这里也有一个帖是介绍了比较多的好的题目的KMP练习题
求Next数组的一个模板, kmp匹配就不写了, 并不是所有时候都需要匹配..Next数组灵活使用比较蛋疼..
#include <cstdio>
#include <cstring>
using namespace std;
const int maxsize = 200050;
int _next[maxsize];
void getNext(char *p)
{
int pLen = strlen(p);
int i, k;
_next[0] = -1;
for(i=1; i<=pLen; i++)
{
k = _next[i-1];
while(k>=0 && p[k] != p[i-1])
k = _next[k];
_next[i] = k+1;
}
}
AC自动机
坑待填.
字符串hash
坑待填
最近做的一些题.
HDU3336
HDU2594
HDU2087
POJ1961
POJ2406
POJ2752
POJ3461
POJ2513
POJ2503
POJ3630
POJ2001
POJ1056
先做别的了.. 这些题代码回头给链接