字符串基础知识

一) kmp

用于一个串的自我匹配或者与另一个串的匹配。

       int j = -1;    next[0] = -1;  //!!!!!!!!!    for(int i = 1; i < lena; i ++){
            while(j >= 0 && a[j + 1] != a[i])j = next[j];
            if(a[j + 1] == a[i])j ++;
            next[i] = j;
        }   j = -1;
        for(int i = 0; i < lenb; i ++){
            while(j >= 0 && a[j + 1] != b[i])j = next[j];
            if(a[j + 1] == b[i])j ++;
            if(j == lena - 1){
                ans ++; j = next[j];
            }

易错点

第一个元素的next值是要特判的, 不然会有问题。

应用

找最小循环节

一个有循环节 k 的串  它从第k + 1 位到第 n 位的next数组是单调的。

当然SA也可以O(n) 找最小循环节, 但代码量差了太多,,

二) ac 自动机

一个串与多个串的匹配。

void insert(char *s){
	int len = strlen(s), now = root;
	for(int i = 0; i < len; i ++){
		if(next[now][s[i] - ‘a‘] == -1)next[now][s[i] - ‘a‘] = newnode();
		now = next[now][s[i] - ‘a‘];
	}end[now] ++;
}
void build(){
	queue<int>q;
	fail[root] = root; //!!!!!!!!!!
	for(int i = 0; i < 26; i ++)
		if(next[root][i] == -1)next[root][i] = root;
		else{
			fail[next[root][i]] = root;
			q.push(next[root][i]);
		}
	while( ! q.empty()){
		int now = q.front(); q.pop();
		for(int i = 0; i < 26; i ++)
			if(next[now][i] == -1)next[now][i] = next[fail[now]][i];
			else{
				fail[next[now][i]] = next[fail[now]][i];
				q.push(next[now][i]);
			}
	}
}

ac自动机把kmp的母串拓展到多个, 即计算一棵Trie树的fail数组

由于fail数组的单调性(p节点的fail一定指向一个深度小于p的点)所以在构造的时候 bfs 一遍就可以了。

一个优化是 如果next[p][c] 为空, 那么把它指向next[f][c] (f是p的所有next[x][c]不为空的祖先x中深度最大的节点)。

因为考虑寻找fail以及匹配的时候如果 next[p][c] 为空, 我们就会把它一直while上去直到一个非空的祖先,但是这一步在很多题中是可以直接在bfs的时候这样预处理出来的。 这样才能保证构建fail指针的复杂度是严格 O(字符串长度) 的。

当然在一些题中这种做法(由于空间限制或者题目需要)是行不通的,这时候只能用不改变next数组的复杂度没有保证的ac自动机, 或者用其它东西来搞啦。

易错点

注意 ac自动机的root 节点的 fail指针也是要特判的!!!

技巧

这个串走到的所有的节点,以及这些节点一路往根fail的节点,都是这个串的子串 (任意一个前缀的任意一个后缀)

1) end数组

end 数组的基本用法是存trie树中的这个点是否是一个字符串的结尾, 但考虑到如果当前节点是当前匹配到的串的后缀, 那么它的后缀还是当前匹配到的串的后缀, 所以在一些题中可以用end数组记录下fail[end], fail[fail[end]] …… root 的信息。 (用 + 或是 |)

2)fail树的构造

在一些题中(经常是要对子串进行修改的时候), 无法只用一个end就记录下fail[end], fail[fail[end]] …… root 的信息。 这时候考虑把所有fail的边反向, 于是就构成了一棵树。然后问题转变为在一棵子树上的一些操作, 这个时候就可以用树状数组线段树之类的来水了。

例, [Noi2011]阿狸的打字机, hdu4117 GRE words, cf 163E

应用

基本的就是 一个(些)(……的)字符串在一个(些)(……的)字符串中出现了多少次

然后似乎就没有什么其它的应用了?

三)后缀数组

时间: 2024-08-10 13:21:43

字符串基础知识的相关文章

python字符串基础知识

1.python字符串可以用"aaa",'aaa',"""aaa""这三种方式来表示 2.python中的转义字符串为"r",C#里面为@逐字符 3.使用index获取字符串的某个字符:如字符串str="abcd123456" ,使用str[3]得到一个字符d 4.使用[startindex:endIndex]截取字符串,如:str[2:3],结果为:c,这里注意的一点:截取的字符串包括开始字符(

列表、字典、字符串基础知识汇总

列表 一.基本的操作 1.增加: 1)变量名.append()方法给列表从最末尾添加值 2)变量名.insert(x,'nihao')方法指定下标位置添加元素 2.删除: 1)变量名.pop(x)方法默认删除最后一个元素,也可以传值,删除指定下标对应的元素 2)变量名.clear()清空列表所有元素 3)del 变量名[下标]删除指定位置的元素也可以删除可变变量和不可变变量 4)变量名.remove(元素)删除列表中的元素 3.修改:通过下标修改列表中的值变量名[X]='nihao' 4.查看:

CSP2019考前字符串基础知识整理

一.string类 1.string的初始化 将string str 赋值为空: string str=""; string str; str.clear(); 或赋值为其他字符串: string str1="absba"; string str2("lqyz"); string s(num,"c");//生成一个字符串,包含num个"c"字符 2.string的遍历 获取第j位置的字符:char ch=s

[Python学习] 专题三.字符串的基础知识

        在Python中最重要的数据类型包括字符串.列表.元组和字典等.该篇主要讲述Python的字符串基础知识. 一.字符串基础         字符串指一有序的字符序列集合,用单引号.双引号.三重(单双均可)引号引起来.如: s1='www.csdn.net'   s2="www.csdn.net"   s3='''aaabbb''' 其中字符串又包括: 1.转义字符串 像C语言中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,pyth

2.1号Java复习题目——Java中的字符串(基础知识整理)

Java中的字符串基础知识 作为程序开发当中,使用最频繁的类型之一,字符串有着与基础类型相同的地位,甚至在 JVM(Java 虚拟机)编译的时候会对字符串做特殊的处理,比如拼加操作可能会被 JVM 直接合成为一个最终的字符串,从而到达高效运行的目的. 1 String 特性 String 是标准的不可变类(immutable),对它的任何改动,其实就是创建了一个新对象,再把引用指向该对象: String 对象赋值之后就会在常量池中缓存,如果下次创建会判定常量池是否已经有缓存对象,如果有的话直接返

python基础知识(字符串)

在我们学习python基础教程的过程中,一般会涉及字符串.列表.元组.字典等基础知识,接下来会将自己学习字符串.列表.元组以及字典的案例等记录如下: 一.字符串: 字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串.创建字符串很简单,只要为变量分配一个值即可.例如: var1="Big data" var2='openstack' 需要注意的是在python里面双引号("")与单引号('')表示的含义完全相同,并不会存在像S

C# 基础知识 (五).变量类型和字符串处理

        这篇文章是阅读<C#入门经典(Beginning C#)>书籍里面的内容,作者Karli Watson.主要包括自己缺乏的一些C#基础知识和在线笔记使用,文章主要包括C#简单变量类型和复杂变量类型.命名规则.隐式转换和显示转换.变量字符串处理等内容,都是非常基础的知识,希望对初学者有所帮助. 一. C#简单变量类型和命名规则         简单类型组成应用程序中基本构件的类型,其中主要有以下类型: 整数类型 sbyte(-128~127之间整数) byte(0~255之间整数

php基础知识总结(3)字符串函数string

一.大小写转换 1.strtolower()--转换为小写. echo strtolower("Hello WORLD!"); //hello world! 2.strtoupper()--转换为大写. echo strtoupper("Hello WORLD!"); //HELLO WORLD! 3.ucfirst()--把首字母转换为大写. echo ucfirst("hello world");     //Hello world 4.uc

java基础知识回顾之---java String final类普通方法的应用之“按照字节截取字符串”

/*需求:在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符.但对应的字节数不同,一个汉字占两个字节.定义一个方法,按照最大的字节数来取子串.如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,那么半个就要舍弃.如果去四个字节就是“ab你”,取五个字节还是“ab你”.*/ 代码:其实是一个解码和编码的问题,要明白UTF-8码表和GBK码表的区别,UTF-8中用三个字节代表一个汉字,GBK使用2个字节代表一个汉字. 且在码表中都是用数字存放这些汉字.