算法随笔——字符串

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define PII pair<int,int>
#define rep(k,a,b) for (int k = a;k <= b;k++)
#define adde(a,b) v[a].push_back(b)
#define addev(a,b,c) v[a].push_back({b,c});
#define rd read
#define all(a) a.begin(),a.end()
#define mem(a,b) memset(a,b,sizeof a);
#define pb push_back
#define vct vector
#define rev(T) reverse(T.begin(),T.end())
#define endl "\n"


int read()
{
	int f=1,k=0;char c = getchar();
	while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
	return k*f;
}

const int N = 2e6+5;

int n ;
string s;
bool ban[N];
int rk[N],sa[N];
int id[N],tmp[N];
int box[N];
int ht[N];
void geth()
{
	//get height
	//height[x] = lcp(sa[x],sa[x-1])
	//height[rk[x]] >= height[rk[x-1]] - 1 即后缀i的height大于等于后缀i-1的height+1
	// lcp 最长公共前缀
	// lcp(sa[i],sa[j]) = min(height(i,j] )
	int len = 0;
	for (int i = 1;i <= n;i++)
	{
		if (len) len--;
		while (s[i+len] == s[sa[rk[i]-1] + len]) len++;
		ht[rk[i]] = len; 
	}
	
	
}

void solvemain()
{
	cin >> n;
	cin >> s;
	
	s = " "+ s;
	//suffix array
	
	//rk the rank of index
	//sa_i = the ith index in suffix array
	int p = 0;//当前不同的排序编号个数
	int maxn = 200;//字符集大小
	for (int i = 1;i <= n;i++) rk[i] = s[i],box[rk[i]]++;//基数排序 
	for (int i = 1;i <= maxn;i++) box[i] += box[i-1];
	for (int i = n;i >= 1;i--)
		sa[box[rk[i]]--] = i;
	
	for (int w = 1;w <= n;w <<= 1,maxn = p)
	{
		int tot = 0;
		for (int i = n-w+1;i <= n;i++) id[++tot] = i;
		for (int i = 1;i <= n;i++) if(sa[i] - w > 0) id[++tot] = sa[i] - w;
		
		mem(box,0);
		for (int i = 1;i <= n;i++) box[rk[i]]++;
		for (int i = 1;i <= maxn;i++) box[i] += box[i-1];
		for (int i = n;i >= 1;i--) //倒序枚举 保证稳定性
			sa[box[rk[id[i]]]--] = id[i];
		memcpy(tmp,rk,sizeof tmp);
		p = 0;
		for (int i =1;i <= n;i++) //重新更新rk
			if (tmp[sa[i]] == tmp[sa[i-1]] && tmp[sa[i]+w] == tmp[sa[i-1] + w])
				rk[sa[i]] = p;
			else rk[sa[i]] = ++p;
		if (p == n) continue;
	}
	
	// for (int i = 1;i <= n;i++) cout<< sa[i] << ' ';
	geth();
	ll ans = n * (n + 1) / 2;
	for (int i = 1;i <= n;i++) 
		ans -= ht[i];
	cout << ans << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;t = 1;
	while(t--)
	{
		solvemain();
	}
	return 0;
}

kmp

kmp 模式串匹配应用条件
1.s[1..n] = T[1..n] 当且仅当所有前缀相同
2.具有传递性
如带有通配符的模式串匹配不可用kmp
ab = acb
a
b = adb
但 acb != adb 不具有传递性

例题 P4696

posted @ 2025-07-26 14:51  codwarm  阅读(7)  评论(0)    收藏  举报