【hdu 6194】string string string

【链接】h在这里写链接


【题意】


给你一个字符串s以及一个整数k;
让你找出这个字符串里面,恰好出现了k次的子串的个数。
k>=1


【题解】


后缀数组题。
对于输入的字符串。求出它的Height数组。
然后预处理出ST表。
便于求区间的最小值。
然后顺序枚举后缀的排名i;
对于height数组。
height[i]实际上包括了两个后缀的信息了。
所以每次处理k-1个height数组。
弄一个k-1长的窗口。(k==1)分类讨论
顺序往右移动.
然后看看这个窗口里面height的最小值temp是多少。
这是至少出现了k次的一个字符串。(也即这段区域里面所有后缀的前temp个字符组成的相同子串)
接下来,就看看height[l]和height[r+1]里面的较大值temp1为多少。
如果比temp小的话。对答案贡献就为temp-temp1;
也即这段区域里面所有后缀的前temp1+1个字符、前temp1+2...前temp个字符组成的字符串
    都恰好出现了k次。
不会重复计算。
因为temp1<temp的话。
下一个窗口的temp就变小了。
它肯定最多为上一个框框的temp1;
则下一个窗口的所有后缀的最长公共前缀只能是前temp1个字符,而上一轮的计数是从前temp1+1个字符开始的。
显然不会有重复的部分。


对于k=1的情况,只不过那个窗口里的最小值。变成了固定的了。
就是这个后缀的长度len了。
然后看看heigh[i]和height


【错的次数】


0

【反思】


在这了写反思

【代码】

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5;
const int MAX_CHAR = 255;//每个数字的最大值。
char s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存
int Sa[N + 10], T1[N + 10], T2[N + 10], C[N + 10];
int Height[N + 10], Rank[N + 10];
int n,k;

const int MAXL = 18;//log2数组的最大长度
const int INF = 0x3f3f3f3f;//数值绝对值的最大值

struct abc {
	int pre2[MAXL + 5], need[N + 10];
	int fmax[N + 10][MAXL + 5], fmin[N + 10][MAXL + 5];

	void init()
	{
		pre2[0] = 1;
		for (int i = 1; i <= MAXL; i++)
		{
			pre2[i] = pre2[i - 1] << 1;
		}
		need[1] = 0; need[2] = 1;
		int temp = 2;
		for (int i = 3; i <= n; i++)//need[i]表示长度为i是2的多少次方,可以理解为[log2i]
			if (pre2[temp] == i)
				need[i] = need[i - 1] + 1, temp++;
			else
				need[i] = need[i - 1];
	}

	void getst(int *a, int n)
	{
		memset(fmax, -INF, sizeof fmax);
		memset(fmin, INF, sizeof fmin);
		for (int i = 1; i <= n; i++)//下标从0开始就改成对应的就好
			fmax[i][0] = fmin[i][0] = a[i];

		for (int l = 1; pre2[l] <= n; l++)
			for (int i = 1; i <= n; i++)
				if (i + pre2[l] - 1 <= n)
					fmax[i][l] = max(fmax[i][l - 1], fmax[i + pre2[l - 1]][l - 1]);

		for (int l = 1; pre2[l] <= n; l++)
			for (int i = 1; i <= n; i++)
				if (i + pre2[l] - 1 <= n)
					fmin[i][l] = min(fmin[i][l - 1], fmin[i + pre2[l - 1]][l - 1]);
	}

	int getmin(int l, int r)
	{
		int len = need[r - l + 1];
		return min(fmin[l][len], fmin[r - pre2[len] + 1][len]);
	}

	int getmax(int l, int r)
	{
		int len = need[r - l + 1];
		return max(fmax[l][len], fmax[r - pre2[len] + 1][len]);
	}

}ST;

void build_Sa(int n, int m) {
	int i, *x = T1, *y = T2;
	for (i = 0; i<m; i++) C[i] = 0;
	for (i = 0; i<n; i++) C[x[i] = s[i]]++;
	for (i = 1; i<m; i++) C[i] += C[i - 1];
	for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i;
	for (int k = 1; k <= n; k <<= 1)
	{
		int p = 0;
		for (i = n - k; i<n; i++) y[p++] = i;
		for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k;
		for (i = 0; i<m; i++) C[i] = 0;
		for (i = 0; i<n; i++) C[x[y[i]]]++;
		for (i = 1; i<m; i++) C[i] += C[i - 1];
		for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i];
		swap(x, y);
		p = 1; x[Sa[0]] = 0;
		for (i = 1; i<n; i++)
			x[Sa[i]] = y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + k] == y[Sa[i] + k] ? p - 1 : p++;
		if (p >= n) break;
		m = p;
	}
}

void getHeight(int n)
{
	int i, j, k = 0;
	for (i = 1; i <= n; i++) Rank[Sa[i]] = i;
	for (i = 0; i<n; i++) {
		if (k) k--;
		j = Sa[Rank[i] - 1];
		while (s[i + k] == s[j + k]) k++;
		Height[Rank[i]] = k;
	}
}

int main()
{
	//freopen("F:\rush.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	while (T--) 
	{
		scanf("%d", &k);
		scanf("%s", s);
		n = strlen(s);
		s[n] = 0;
		build_Sa(n + 1, MAX_CHAR);
		getHeight(n);
		ST.init();
		ST.getst(Height, n);
		//按照k==1以及k!=1两种情况分一下类
		long long ans = 0;
		if (k == 1)
		{
			for (int i = 1; i <= n; i++) 
			{
				int temp = n - Sa[i];
				int temp1 = max(i == 1 ? 0 : Height[i], i == n ? 0 : Height[i + 1]);
				if (temp > temp1)
					ans += temp - temp1;
			}
		}
		else
		{
			int l = 2;//左端点一开始等于l
			for (int r = k; r <= n; r++)//枚举右端点在什么位置
			{
				//k-1个就凑够k次了
				int temp = ST.getmin(l, r);//求出[l..r]的最小值
				int temp1 = max(Height[l-1], r == n ? 0 : Height[r + 1]);
				//l..r
				//对应了
				//l-1,l..r这些字符串
				//Height[l-1]和Height[r+1]
				
				if (temp > temp1)
					ans += temp - temp1;
				l++;
			}
		}
		printf("%lld
", ans);
	}
	return 0;


原文地址:https://www.cnblogs.com/AWCXV/p/7625995.html