【笔记】字符串基础
输入&输出
一般的东西就不说了
-
先讲读入一整行
-
cinstd :: cin.getline(s,max_length);其实
cincout这两个类里面有好多好东西。 -
scanfscanf("%[^\n]s",s);//其实 scanf("%[^\n]",s); 也行就是了我之前完全不知道
scanf可以这么用,总之[]里面是允许(加^是不允许)读入的字符集。 -
gets或fgets很简单粗暴的用法
gets(s); fgets(s,max_length,stdin);我看完这个之后才理解
stdin也能算是(存疑)一个文件指针?
-
-
特化字符串 STL:string
字符串匹配
KMP
前缀数组:
我们记字符串 \(s[N]\) 的前缀为 \(s_i\),并记 \(s_i\) 的前缀为 \(pre_{i,j}\),后缀为 \(suf_{i,j}\)。
前缀数组的定义就是:
即前/后缀的最大重合长度。
这个东西暴力就是 \(\cal{O}(n^2)\) 的。
但若已知 \(\pi[0] \sim \pi[k-1]\),是否有更好的办法求 \(\pi[k]\) 呢?
-
如果接着 \(k-1\) 位的最大前/后缀,他们后面的字符是匹配的,那么直接 \(\pi[k] \gets \pi[k-1]+1\) 就好;
-
如果不能匹配,我们也要找一个 \(k-1\) 位时的次大的能够匹配前/后缀,再看看他们后面的字符是否匹配。
那次大的前/后缀,不就是最大的前/后缀的最大前/后缀嘛,直接用 \(\pi[\pi[k-1]]\) 就好了!
举一个例子看看:
abacabab
当到最后一个 b 的时候,显然之前的最大前/后缀 aba 加上后面的字符后就变成了 abac 和 abab,不相匹配。那我们找次大前后缀,即 aba 的最大前/后缀 a,发现 ab 是相互匹配的,于是 \(\pi[7] = 2\)。
代码环节:
const int N = 100010;
int prefix[N];
char s[N];
void get_prefix() {
prefix[0] = -1;
for(int i = 1, j = -1;s[i];++i) {
while(s[i] != s[j+1]&&j >= 0)
j = prefix[j];
if(s[i] == s[j+1]) ++j;
prefix[i] = j;
}
}
这里采用的 \(\pi[i]\) 其实是上文中 \(\pi[i]-1\)。
复杂度分析:
其他都比较明了,主要是 while 段为什么最后会是 \(\cal{O}(n)\) 的。因为 \(j\) 在每次循环时最多只能自增一次,而每次跳 \(j \gets \pi[j]\) 时 \(j\) 至少减少 \(1\),故此环节只会执行 \(\cal{O}(n)\) 次。
KMP:
问字符串 \(pattern[N]\) 在 \(text[M]\) 中出现了多少次。
很简单,把两个字符串放一起,中间加一个分隔符,求一遍前缀数组,看看有多少个 \(\pi[i] = N\) 就好了。
Z 函数
AC 自动机
刚才 KMP 解决的是单个模式串的情况,当我们有多个模式串的时候,是否可以有类似于前缀数组一样的东西呢?
实际上是可以的,只不过原来是一个串,现在是一棵 trie 树。原来的前缀数组现在我们叫他失配指针,算失配指针的思路也是类似的。

浙公网安备 33010602011771号