加载中…

返回上一页

字符串基础

字符串哈希(hash)

字符串哈希,其实就是类似于一个数组前缀和的一个东西,能够快速判断字符串的相等情况.

首先是用一个 unsigned 的自然溢出,因为模运算是非常慢的,所以要用到这么个自然溢出.

由于字符串是有序的,在判等的时候不能直接加数,否则是会被误认为相等的. 这时需要引入一个 base,一般选一个素数. 每次乘上这个素数,这样就是有序的了.

至于 base 取多少,多少其实都可以,只要不被卡就行 我一般用 131.

这是一个模板.

#define ull unsigned ll
#define base 131
char s[maxn];
ll hs[maxn];
inline void Hash()
{
	for(rll i=1;i<=strlen(s+1);i++)
		hs[i]=hs[i-1]*base+s[i];
}

怎么查询?

不能直接减去前面的,应该把要查询的这个串的前面串哈希的前缀和乘以一个 base 的要查询的字符串长度次方,再减(想一想,为什么?).

inline void gethash(rll l,rll r)
{
	return hs[r]-hs[l-1]*ksm(base,r-l+1);// ksm指快速幂
}

KMP

kmp 一般用来处理询问的字符串在原串中出现了几次的问题.

它的重点就在于找到最长相同前后缀长度以及失配位置.

首先需要预处理出模板串的一个next数组,就是上面所说的那个模板串最长相同的前缀后缀长度.

在模板串和询问串匹配的时候,如果出现失配情况,由于前面的next数组已经处理出来了,所以每次失配的时候直接跳next就可以了.

实现:

// t为模板串,m是它的长度;s为匹配串,n是它的长度
for(rll i=1,j=0;i<=n;i++)
{
	while(j&&(t[j+1]^s[i])) j=nxt[j];
	if(t[j+1]==s[i]) j++;
	if(j==m)
	{
		// i-j+1 即为所求的一个长度
		j=nxt[j];
	}
}

如何求 next 数组?

还是设 i 代表当前位置,j 代表 next 的位置. 令第一个位置的next值为 0. 不断移动 i 指针,如果 t[i]=t[j+1],那么它们一定是最长的相同前(后)缀(想一想,为什么?). 如果不相等,那就反复进行上面的操作直至它们相同或者next[i]=0(没有可匹配的了)为止.

for(rll i=2,j=0;i<=m;i++)
{
	while(j&&(t[j+1]^t[i])) j=nxt[j];
	if(t[j+1]==t[i]) j++;
	nxt[i]=j;
}

trie

就是字典树.

它利用树形结构来确保复杂度. 插入和查找都是带 log 的.

具体做法就是每一个位置都是一层,那么每一次插入一个字母,就是往下了一层. 如果这一层已有这个字母,则跳过;否则,新建一个节点与上面连边. 根节点多设一个就行了.

在单词结尾的时候,打一个标记,证明这里是一个单词.

插入操作的实现:

ll trie[maxn][26],cnt;
bool tag[maxn];
inline void ins(rg char* s)
{
	rll x=0;
	for(rll i=1;i<=strlen(s+1);i++)
	{
		if(!trie[tot][s[i]^'0']) trie[tot][s[i]-'0']=++cnt;
		x=trie[x][s[i]^'0'];
	}
	tag[x]=1;
}

查询也很简单,直接for一遍即可.

for(rll i=cnt;~i;i--)
{
	for(rll j=0;j<26;j++)
	{
		// trie[i][j];
	}
	// tag[i];
}
posted @ 2022-10-05 16:23  1Liu  阅读(7)  评论(0)    收藏  举报