题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6194

题意:告诉你一个字符串和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的情况的,k=1的时候我们直接特判掉,k=1的时候不同字符串个数就是n-sa[i]。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int sa[maxn];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
//的后缀的开头位置顺次放入SA中
int t1[maxn],t2[maxn],c[maxn];//求SA数组需要的中间变量,不需要赋值
int Rank[maxn],height[maxn];
//待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
//除s[n-1]外的所有s[i]都大于0,r[n-1]=0
//函数结束以后结果放在sa数组中
void build_sa(int s[],int n,int m)
{
int i,j,p,*x=t1,*y=t2;
//第一轮基数排序,如果s的最大值很大,可改为快速排序
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(j=1;j<=n;j<<=1)
{
p=0;
//直接利用sa数组排序第二关键字
for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
//这样数组y保存的就是按照第二关键字排序的结果
//基数排序第一关键字
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];
//根据sa和x数组计算新的x数组
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]+j]==y[sa[i]+j]?p-1:p++;
if(p>=n)break;
m=p;//下次基数排序的最大值
}
}
void getHeight(int s[],int n)
{
int i,j,k=0;
for(i=0;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 n,k,dp[maxn][30];
int r[maxn];
char s[maxn];
void Lcp_init(){
for(int i=1; i<=n+1; i++) dp[i][0] = height[i];
for(int j=1; (1<<j)<=n+1; j++){
for(int i=0; i+(1<<j)<n+2; i++){
dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
}
}
int lcp(int l, int r){
if(l == r) return n - sa[r];
if(l>r) swap(l, r);
++l;
int k=0,len=r-l+1;
while((1<<(k+1))<=len) ++k;
return min(dp[l][k], dp[r-(1<<k)+1][k]);
} int main()
{
int T;
scanf("%d", &T);
while(T--){
scanf("%d", &k);
scanf("%s", s);
n = strlen(s);
for(int i=0; i<n; i++) r[i]=s[i]-'a'+1;
r[n]=0;
build_sa(r, n+1, 128);
getHeight(r, n);
Lcp_init();
LL ans = 0;
for(int i=1; i+k-1<=n; i++){
ans += lcp(i, i+k-1);
if(i-1 > 0) ans -= lcp(i-1,i+k-1);
if(i+k <= n) ans -= lcp(i, i+k);
if(i-1 >0 && i+k<=n) ans += lcp(i-1, i+k);
}
printf("%lld\n", ans);
}
return 0;
}

HDU 6194 string string string 2017沈阳网络赛 后缀数组的更多相关文章

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

    转自:http://blog.csdn.net/aozil_yang/article/details/77929216 题意: 告诉你一个字符串和k , 求这个字符串中有多少不同的子串恰好出现了k 次 ...

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

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6197 题意:给你n个数,问让你从中删掉k个数后(k<=n),是否能使剩下的序列为非递减或者非递增 ...

  3. HDU 6194 string string string ——(2017沈阳网络赛,后缀数组)

    思路见:http://blog.csdn.net/aozil_yang/article/details/77929216. 代码如下: #include <stdio.h> #includ ...

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

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6200 题意:给个图,有2种操作,一种是加一条无向边,二是查询u,v之间必须有的边的条数,所谓必须有的边 ...

  5. HDU 6199 2017沈阳网络赛 DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6199 题意:n堆石子,Alice和Bob来做游戏,一个人选择取K堆那么另外一个人就必须取k堆或者k+1 ...

  6. HDU 6203 2017沈阳网络赛 LCA,DFS+树状数组

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6203 题意:n+1 个点 n 条边的树(点标号 0 ~ n),有若干个点无法通行,导致 p 组 U V ...

  7. HDU 6205 2017沈阳网络赛 思维题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6205 题意:给你n堆牌,原本每一堆的所有牌(a[i]张)默认向下,每次从第一堆开始,将固定个数的牌(b ...

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

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6198 题意:给出一个数k,问用k个斐波那契数相加,得不到的数最小是几. 解法:先暴力打表看看有没有规律 ...

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

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6201 题意:给出一棵树,每个点有一个权值,代表商品的售价,树上每一条边上也有一个权值,代表从这条边经过 ...

随机推荐

  1. 洛谷 P2647 最大收益

    我是题面 恩,贪心,鉴定完毕. 一个物品是否放进来,取决于它是否能对答案做出贡献. 那物品i的贡献就是\(w[i]-r[i]\) 可是收益的减少是会叠加的 那就是\(w[i]-j*r[i]\),j表示 ...

  2. 创建 cachingConfiguration 的配置节处理程序时出错: 未能加载文件或

    C:\Users\xxx\Documents\IISExpress\config\applicationhost.config 将这里面带的项目路径替换成你当前路径 {"创建 caching ...

  3. CF662C Binary Table 枚举 FWT

    题面 洛谷题面 (虽然洛谷最近有点慢) 题解 观察到行列的数据范围相差悬殊,而且行的数量仅有20,完全可以支持枚举,因此我们考虑枚举哪些行会翻转. 对于第i列,我们将它代表的01串提取出来,表示为\( ...

  4. [CTSC2012]熟悉的文章 后缀自动机

    题面:洛谷 题解: 观察到L是可二分的,因此我们二分L,然后就只需要想办法判断这个L是否可行即可. 因为要尽量使L可行,因此我们需要求出对于给定L,这个串最多能匹配上多少字符. 如果我们可以对每个位置 ...

  5. CF825F String Compression 解题报告

    CF825F String Compression 题意 给定一个串s,其中重复出现的子串可以压缩成 "数字+重复的子串" 的形式,数字算长度. 只重复一次的串也要压. 求压缩后的 ...

  6. portal商品展示功能逻辑

    看下接口: 返回值: 门户商品搜索功能的实现: 根据分类id进行搜索,根据关键词进行搜索,并按照一定的顺序排序 业务逻辑: 1.查询分类是否存在. 2.如果分类存在,则递归分类,展示父类商品,子类商品 ...

  7. duilib 使用图片素材或者算法给窗体增加阴影(源码和demo)

    转载请说明原出处,谢谢:http://blog.csdn.net/zhuhongshu/article/details/42580877 之前我写的程序使用阴影时,一直是使用codeproject网站 ...

  8. OpenCV---膨胀与腐蚀

    膨胀 腐蚀 一:膨胀实现dilate import cv2 as cv import numpy as np def dilate_demo(image): #膨胀 print(image.shape ...

  9. [LeetCode] 27. Remove Element ☆

    Given an array and a value, remove all instances of that value in place and return the new length. D ...

  10. Lucene 索引与检索架构图