【转】HDU 6194 string string string (2017沈阳网赛-后缀数组)

转自:http://blog.csdn.net/aozil_yang/article/details/77929216

题意:

告诉你一个字符串和k , 求这个字符串中有多少不同的子串恰好出现了k 次。

思路:

后缀数组。

我们先考虑至少出现k 次的子串, 所以我们枚举排好序的后缀i (sa[i]) 。

k段k 段的枚举。

假设当前枚举的是 sa[i]~sa[i + k -1]

那么假设这一段的最长公共前缀  是L 的话。

那么就有L 个不同的子串至少出现了k次。

我们要减去至少出现k + 1次的 , 但还要和这个k 段的lcp 有关系, 因此肯定就是 这一段 向上找一个后缀 或者向下找一个后缀。

即  sa[i-1] ~ sa[i + k - 1]  和 sa[i] ~ sa[i + k] 求两次lcp 减去即可。

但是会减多了。

减多的显然是sa[i-1] ~ sa[i + k] 的lcp。 加上即可。

注意 k =1的情况在求lcp 会有 问题, 即求一个串的最长公共前缀会有问题, 特判一下即可。

一定要注意边界问题 边界问题 边界问题!!!

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5
  6 const int maxn = 100000 + 10;
  7
  8 int t1[maxn], t2[maxn], c[maxn];
  9
 10 bool cmp(int* r, int a,int b,int l){
 11     return r[a] == r[b] && r[a+l] == r[b+l];
 12 }
 13
 14 void da(int str[], int sa[], int Rank[], int lcp[], int n, int m){
 15     ++n;
 16     int i, j, p, *x = t1, *y = t2;
 17     for (i = 0; i < m; ++i) c[i] = 0;
 18 //        puts("hha");
 19     for (i = 0; i < n; ++i) c[x[i] = str[i] ]++;
 20     for (i = 1; i < m; ++i) c[i] += c[i-1];
 21     for (i = n-1; i >= 0; --i) sa[--c[x[i] ] ] = i;
 22     for (j = 1; j <= n; j <<= 1){
 23         p = 0;
 24         for (i = n-j; i < n; ++i) y[p++] = i;
 25         for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
 26         for (i = 0; i < m; ++i) c[i] = 0;
 27         for (i = 0; i < n; ++i) c[x[y[i] ] ]++;
 28
 29         for (i = 1; i < m; ++i) c[i] += c[i-1];
 30         for (i = n-1; i >= 0; --i) sa[--c[x[y[i] ] ] ] = y[i];
 31
 32         swap(x,y);
 33         p = 1; x[sa[0] ] = 0;
 34         for (i = 1; i < n; ++i){
 35             x[sa[i] ] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
 36
 37
 38         }
 39
 40         if (p >= n)break;
 41         m= p;
 42
 43
 44     }
 45
 46     int k = 0;
 47     n--;
 48     for (i = 0; i <= n; ++i) Rank[sa[i] ] = i;
 49     for (i = 0; i < n; ++i){
 50         if (k)--k;
 51         j = sa[Rank[i]-1 ];
 52         while(str[i+k] == str[j+k])++k;
 53         lcp[Rank[i] ] = k;
 54     }
 55 }
 56
 57 int lcp[maxn], a[maxn], sa[maxn], Rank[maxn];
 58
 59 char s[maxn];
 60
 61 int d[maxn][40];
 62 int len;
 63
 64 void rmq_init(int* A, int n){
 65     for (int i = 0; i < n; ++i) d[i][0] = A[i];
 66     for (int j = 1; (1<<j) <= n; ++j)
 67         for (int i = 0; i + (1<<j) - 1 < n ; ++i)
 68             d[i][j] = min(d[i][j-1], d[i + (1<< (j-1))][j-1]);
 69 }
 70
 71 int ASK(int l,int r){
 72     int k = 0;
 73     while((1<<(k+1)) <= r-l + 1)++k;
 74     return min(d[l][k], d[r-(1<<k) + 1][k]);
 75 }
 76
 77 int ask(int l,int r){
 78     if (l == r) return len - sa[r]; /// l == r的话 是一个串, 返回本身的长度即可。
 79     return ASK(l + 1, r); ///否则在rmq查询。
 80 }
 81
 82 //
 83 int main(){
 84     int T;
 85     scanf("%d", &T);
 86
 87     while(T--){
 88         int k;
 89         scanf("%d", &k);
 90         scanf("%s", s);
 91         len = strlen(s);
 92         for (int i = 0; i < len; ++i){
 93             a[i] = s[i] - ‘a‘ + 1;
 94         }
 95         a[len] = 0;
 96         da(a, sa, Rank, lcp, len, 30);
 97         rmq_init(lcp, len + 1);
 98         long long ans = 0;
 99         for (int i = 1; i + k - 1 <= len; ++i){
100             ans += ask(i, i + k - 1);
101             if (i - 1 > 0)ans -= ask(i - 1, i + k - 1); ///注意边界问题。
102             if (i + k <= len)ans -= ask(i, i + k);
103             if (i - 1 > 0 && i + k <= len)ans += ask(i - 1 , i + k);
104         }
105         printf("%I64d\n", ans);
106
107
108     }
109     return 0;
110 }
111 
时间: 2024-10-08 20:04:29

【转】HDU 6194 string string string (2017沈阳网赛-后缀数组)的相关文章

HDU 6197 array array array 2017沈阳网络赛 LIS

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6197 题意:给你n个数,问让你从中删掉k个数后(k<=n),是否能使剩下的序列为非递减或者非递增序列 解法:签到题,就是让你求最长不下降子序列长度len,然后判断下n-len是否小于k(将序列反着存下来然后再求即最长不上升子序列,取两者len中的较大值),然后直接套nlogn的模板即可. #include <bits/stdc++.h> using namespace std; const

HDU 6229 Wandering Robots(2017 沈阳区域赛 M题,结论)

题目链接  HDU 6229 题意 在一个$N * N$的格子矩阵里,有一个机器人. 格子按照行和列标号,左上角的坐标为$(0, 0)$,右下角的坐标为$(N - 1, N - 1)$ 有一个机器人,初始位置为$(0, 0)$. 现在这个矩阵里面,有一些障碍物,也就是说机器人不能通过这些障碍物. 若机器人当前位置为$(x, y)$,那么他下一个位置有可能为与当前格子曼哈顿距离为$1$的所有格子的任意1个. 也有可能停留在原来的位置$(x, y)$ 求经过无限长的时间之后,这个机器人的位置在给定区

HDU 6205 2017沈阳网络赛 思维题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6205 题意:给你n堆牌,原本每一堆的所有牌(a[i]张)默认向下,每次从第一堆开始,将固定个数的牌(b[i]张)翻上,然后下一堆继续,直到没有足够的牌翻上,然后你可以获得当前已经操作过的堆的所有牌.最初你可以调整堆的顺序,把第一堆放到最后一堆(逆时针旋转),你可以重复这个操作,问你要重复多少次这个操作,才能获得最多的牌. 解法:先把这个序列复制一遍放在原来的序列后面.当i=n的时候结束就可以了,每次

HDU 6200 2017沈阳网络赛 树上区间更新,求和

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6200 题意:给个图,有2种操作,一种是加一条无向边,二是查询u,v之间必须有的边的条数,所谓必须有的边就是对于u,v必须通过这条边才能到达. 解法:一个很简单的想法,搞出图上的一颗树,然后剩下的边当成询问点队加到更新点集,每加入一个更新点对,直接把u,v区间的值置为0即可,查询就直接区间求和,可以直接树剖来维护,简单暴力,读入挂卡过.还有1个log的做法,可以用LCT维护(这个没写,口胡的) #in

HDU 6198 2017沈阳网络赛 线形递推

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6198 题意:给出一个数k,问用k个斐波那契数相加,得不到的数最小是几. 解法:先暴力打表看看有没有规律. #include <bits/stdc++.h> using namespace std; int dp[2000][2000]; typedef long long LL; int main() { LL c[50]; c[0]=0; c[1]=1; c[2]=1; for(int i=2;

HDU 6201 2017沈阳网络赛 树形DP或者SPFA最长路

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6201 题意:给出一棵树,每个点有一个权值,代表商品的售价,树上每一条边上也有一个权值,代表从这条边经过所需要的花费.现在需要你在树上选择两个点,一个作为买入商品的点,一个作为卖出商品的点,当然需要考虑从买入点到卖出点经过边的花费.使得收益最大.允许买入点和卖出点重合,即收益最小值为0. 解法:我们设1为根节点,假设一开始一个人身上的钱为0.我们设dp[i][0]表示从根节点走到i及其子树并中任一点买

ACM学习历程—HDU 5459 Jesus Is Here(递推)(2015沈阳网赛1010题)

Sample Input 9 5 6 7 8 113 1205 199312 199401 201314 Sample Output Case #1: 5 Case #2: 16 Case #3: 88 Case #4: 352 Case #5: 318505405 Case #6: 391786781 Case #7: 133875314 Case #8: 83347132 Case #9: 16520782 题目要求当前字符串序列中某项里cff前缀两两间差值的和. 假设已经纪录了cff前缀的

hdu6223 Infinite Fraction Path 2017沈阳区域赛G题 bfs加剪枝(好题)

题目传送门 题目大意:给出n座城市,每个城市都有一个0到9的val,城市的编号是从0到n-1,从i位置出发,只能走到(i*i+1)%n这个位置,从任意起点开始,每走一步都会得到一个数字,走n-1步,会得到一个长度为n的数列,输出能得到的最大的数列(当成数字). 思路: 一个数字肯定是最高位越大,这个数字本身就越大,所以肯定第一位要取最大值,在这一位取最大值的时候后面每一位都要尽量最大,所以想到bfs. 但是bfs肯定要剪枝,怎么剪枝呢? 1.按照思路,我要取每一位尽可能大的值,所以某一个状态的某

hdu 5455 (2015沈阳网赛 简单题) Fang Fang

题目;http://acm.hdu.edu.cn/showproblem.php?pid=5455 题意就是找出所给字符串有多少个满足题目所给条件的子串,重复的也算,坑点是如果有c,f以外的字符也是不满足条件的,还有我被坑了的地方就是当输入很多f的时候,我尽然脑抽的 认为这是不满足条件的,注意这两点就行了,直接暴力 1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 int main() 5 { 6 int