【hdu 6194】string string string
【链接】h在这里写链接
【题意】
给你一个字符串s以及一个整数k;
让你找出这个字符串里面,恰好出现了k次的子串的个数。
k>=1
让你找出这个字符串里面,恰好出现了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
对于输入的字符串。求出它的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\n", ans); } return 0;