字符串算法:Hash算法,KMP算法,最小循环节、循环周期,Manacher马拉车算法

#字符串
Ⅰ输入输出方式:
1.char s[]
scanf()
printf()
gets()
2.String
cin
cout
sstream(可用于非高精度输出x^y)

Hash:

1.工程中常见的hash算法:
md5 sha1 sha256
2.Rabin-Karp:求出每个前缀的hash,O(1)计算出一个子串的hash值
Hash[i] = (hash[i - 1]*p+s[i])%mod
取模方式:
p为大质数,mod为超大质数。
p 超过字符集大小,奇数 13131
base和mod互质
(要注意模数的限制)mod一个自己都不知道的数,眼睛闭上随便按。
3.对于Codeforces 赛制允许Hack
(1) srand(time(0));
int p = 1e15 + rand();
(2)
利用CF不能复制代码
在程序里面打一个10k的表
求和作为p
(3)原理
相同的字符串,hash一定相同
不同的字符串,hash尽量不同
hash相同,字符串 很可能相同 直接认为是相同的
hash不同,字符串 一定不同
4. hash常与二分结合(二分字符串长度)
5. map, set, multimap, and multiset 上述四种容器采用红黑树实现,红黑树是平衡二叉树的一种。不同操作的时间复杂度近似为:
插入: O(logN) 查看:O(logN) 删除:O(logN)
6. hash_map, hash_set, hash_multimap, and hash_multiset
上述四种容器采用哈希表实现,不同操作的时间复杂度为:
插入:O(1),最坏情况O(N)。
查看:O(1),最坏情况O(N)。
删除:O(1),最坏情况O(N)。 如果你采用合适的哈希函数,你可能永远不会看到最坏情况。

Kmp:O(n+m)充分利用模式串的已知信息

nxt[i] = max{j|j<i&&p[1..j]==p[i-j+1...i]}
(待改)/*
void get_nxt(){
	int i = 0,j = -1;
	nxt[i] = j;
	while(i < lenp){
		if(j == -1 || p[i] == p[j]) {
			++i,++j;
			if(p[i] == p[j])	nxt[i] = nxt[j];//优化:只用于文本串匹配
			else nxt[i] = j;
		}
		else 	j = nxt[j];
	}
}
int kmp(){
	get_nxt();
	int i = 0,j = 0; 
	while(i < lent && j < lenp){
		if(j == -1 || t[i] == p[j]){++i,++j;}
		else j = nxt[j];
	}
	if(j == lenp) return i - lenp + 1;
	return -1;
}
*/
(正常情况)/*
void get_nxt(){
	int i = 0,j = -1;//周期,循环节从0开始做 
	nxt[0] = -1;
	while(i <= lenp){
		if(j == -1 || p[i] == p[j]){
			++i;++j;
			nxt[i] = j;//不能加优化
		}
		else j = nxt[j];
	}
}
*/

KMP最小循环节、循环周期

定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。
(1)如果len可以被len - next[len]整除,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。
(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数L-len%L=L-(len-L)%L=L-next[len]%L
ⅣExtend_kmp:

Nxt[i]=lcp(p[i...len]&&p)
Extnd[i] = lcp(t[i..len]&&p)
/*
void get_nxt(){
	int i = 0,j,a;
	nxt[0] = lenp;
	while(p[i] == p[i + 1] && i + 1 < lenp)	++i;
	nxt[1] = i;
	a = 1;
	for(int i = 2; i < lenp; ++i){
		if(nxt[i - a] + i < a + nxt[a])	nxt[i] = nxt[i - a];//a + nxt[a] 即 P 
		else{//nxt[i - a] + i == a + nxt[a]
			j = max(0,a + nxt[a] - i);
			while(i + j < lenp && p[j] == p[i + j]) ++j;
			a = i;nxt[i] = j;
		}
	}
}
void get_extnd(){
	get_nxt();
	int i = 0,j,a;
	while(p[i] == t[i] && i < lenp && i < lent) ++i;
	extnd[0] = i;
	a = 0;
	for(int i = 1; i < lent; ++i){
		if(nxt[i - a] + i < a + extnd[a])	extnd[i] = nxt[i - a];
		else {
			j = max(0,a + extnd[a] - i);
			while(i + j < lent && j < lenp && t[i + j] == p[j]) ++j;
			a = i; extnd[i] = j;
		}
	}
}
*/

Ⅴ最小表示法
给一个字符串S,求S的最小表示法。 对一个字符串,进行如下两种操作:

  1. 把头字符放到尾 2. 把尾字符放到头
    不断进行上述2个操作,直到S的字典序达到最小,此时的S是原串的最小表示法
    比如bbabc,最小表示法是abcbb

Manacher:

马拉车算法可以看成是中心检测法(区分奇偶)的升级版本
预处理1:
在所有的空隙位置(包括首尾)插入同样的不会出现的字符。无论原字符串是奇数还是偶数,通过这种做法,都会使得处理过的字符串变成奇数长度。
比如:
123(长度为3) -> #1#2#3# (长度为7)
Len[i] 表示以第 i 个字符为对称轴的回文串的回文半径(文串中最左或最右位置的字符与其对称轴的距离称为回文半径)
2.在字符串的首尾处各加入一个字符,例如 ~ 和 +,只要两个字符不相同,就没有可能成为最长字符串的一部分,不会对结果造成影响:
123(长度为3) -> ~#1#2#3#+ (长度为9)
abccba (长度为6)-> ~#a#b#c#c#b#a#+(长度为15)
ac自动机

后缀数组
后缀自动机

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值