HDU 5769 后缀数组
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5769 [2016多校contest-4]
题意:给定一个字符,还有一个字符串,问这个字符串存在多少个不同的子串并且子串含有给定的字符。
思路:先忽略包含某个字符的问题,只求一个字符串不同的子串个数,根据<<后缀数组——处理字符串的有力工具>>,每个子串一定是某个后缀的前缀, 那么一个字符串不同的子串个数等价于求所有后缀之问的不相同的前缀的个数 。如果所有的后缀按照 suffix(sa[1]), suffix(sa[2]), suffix(sa[3]),……,suffix(sa[n])的顺序计算,不难发现,对于每一次新加进来的后缀 suffix(sa[k]),它将产生 n-sa[k]+1个新的前缀。 但是其中有height[k]个是和前面的字符串的前缀是相同的。 所以 suffix(sa[k])将 ''贡献" 出 n-sa[k]+1- height[k]个不同的子串。累加后便是答案。
然后考虑拥有某个字符的子串,如果要求字符X,只需要记录距离sa[i]最近的字符X的位置(用nxt[sa[i]]表示)即可,即该题的结果为
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<queue> #include<vector> #include<time.h> #include<cmath> using namespace std; typedef long long int LL; const int MAXN = 100000 + 5; int cmp(int *r, int a, int b, int l){ return r[a] == r[b] && r[a + l] == r[b + l]; } int wa[MAXN], wb[MAXN], wv[MAXN], WS[MAXN]; void da(int *r, int *sa, int n, int m){ int i, j, p, *x = wa, *y = wb, *t; for (i = 0; i < m; i++) { WS[i] = 0; } for (i = 0; i < n; i++) { WS[x[i] = r[i]]++; } for (i = 1; i < m; i++) { WS[i] += WS[i - 1]; } for (i = n - 1; i >= 0; i--) { sa[--WS[x[i]]] = i; } for (j = 1, p = 1; p<n; j *= 2, m = p) { for (p = 0, i = n - j; i < n; i++) { y[p++] = i; } for (i = 0; i < n; i++) { if (sa[i] >= j){ y[p++] = sa[i] - j; } } for (i = 0; i < n; i++) { wv[i] = x[y[i]]; } for (i = 0; i < m; i++) { WS[i] = 0; } for (i = 0; i < n; i++) { WS[wv[i]]++; } for (i = 1; i < m; i++) { WS[i] += WS[i - 1]; } for (i = n - 1; i >= 0; i--) { sa[--WS[wv[i]]] = y[i]; } for (t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++){ x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; } } return; } int Rank[MAXN], height[MAXN],sa[MAXN]; void calheight(int *r, int *sa, int n){ int i, j, k = 0; for (i = 1; i <= n; i++) { Rank[sa[i]] = i; } for (i = 0; i < n; height[Rank[i++]] = k){ for (k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++); } return; } int t, len,r[MAXN],nxt[MAXN],Ca=1; char str[MAXN],ch[2]; LL solve(int n){ LL ans = 0; for (int i = 1; i <= n; i++){ ans += 1LL*(n - max(nxt[sa[i]], sa[i] + height[i])); } return ans; } bool makeNxt(int n){ bool flag = false; int index = n; for (int i = n - 1; i >= 0; i--){ if (str[i] == ch[0]){ flag = true; index = i; } nxt[i] = index; } return flag; } int main(){ //#ifdef kirito // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); //#endif // int start = clock(); scanf("%d", &t); while (t--){ scanf("%s", ch); scanf("%s", str); len = strlen(str); for (int i = 0; i <= len; i++){ if (i == len){ r[i] = 0; continue; } r[i] = (str[i]-'a'+1); } if (!makeNxt(len)){ printf("Case #%d: 0\n", Ca++); continue; } da(r, sa, len+1, 30); calheight(r, sa, len); printf("Case #%d: %I64d\n", Ca++,solve(len)); } //#ifdef LOCAL_TIME // cout << "[Finished in " << clock() - start << " ms]" << endl; //#endif return 0; }