http://poj.org/problem?id=3415

题意:
求长度不小于K的公共子串的个数。

思路:
好题!!!拉丁字母让我Wa了好久!!单调栈又让我理解了好久!!太弱啊!!

最简单的就是暴力枚举,算出LCP,那么这个LCP对答案的贡献就是$x-k+1$。

我们可以将height进行分组,大于等于k的在同一组,如果两个后缀的最长公共子串>=k,那么它们肯定在同一个组内。现在从头开始扫,每遇到A的后缀时,就统计一下它和它前面的B的后缀能组成多少长度>=k的公共子串,然后再反过来处理B的后缀即可,一共需要扫两遍。但是这样的时间复杂度是O(n^2),是行不通的。

因为两个后缀的LCP是它们之间的最小height值,所以可以维护一个自底向上递增的单调栈,如果有height值小于了当前栈顶的height值,那么大于它的那些只能按照当前这个小的值来计算。这样分别处理两次,一次处理A的后缀,一次处理B的后缀。需要记录好栈里的和以及每个组内的后缀数。

 #include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int INF = 0x3f3f3f3f;
const int maxn=*1e5+; int n;
char s[maxn],s1[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int Rank[maxn],height[maxn];
int sta[maxn],cnt[maxn]; void build_sa(int m)
{
int *x=t,*y=t2;
//基数排序
for(int i=;i<m;i++) c[i]=;
for(int i=;i<n;i++) c[x[i]=s[i]]++;
for(int i=;i<m;i++) c[i]+=c[i-];
for(int i=n-;i>=;i--) sa[--c[x[i]]]=i;
for(int k=;k<=n;k<<=)
{
int p=;
//直接利用sa数组排序第二关键字
for(int i=n-k;i<n;i++) y[p++]=i;
for(int i=;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
//基数排序第一关键字
for(int i=;i<m;i++) c[i]=;
for(int i=;i<n;i++) c[x[y[i]]]++;
for(int i=;i<m;i++) c[i]+=c[i-];
for(int i=n-;i>=;i--) sa[--c[x[y[i]]]]=y[i];
//根据sa和y计算新的x数组
swap(x,y);
p=;
x[sa[]]=;
for(int i=;i<n;i++)
x[sa[i]]=y[sa[i-]]==y[sa[i]]&&y[sa[i-]+k]==y[sa[i]+k]?p-:p++;
if(p>=n)
break;
m=p; //下次基数排序的最大值
}
} void getHeight(int n)
{
int i,j,k=;
for(i=;i<=n;i++) Rank[sa[i]]=i;
for(i=;i<n;i++)
{
if(k) k--;
int j=sa[Rank[i]-];
while(s[i+k]==s[j+k]) k++;
height[Rank[i]]=k;
}
} int main()
{
int k;
//freopen("in.txt","r",stdin);
while(~scanf("%d",&k))
{
if(!k) break;
scanf("%s%s",s,s1);
int len1 = strlen(s);
int len2 = strlen(s1);
s[len1]='@';
for(int i=;i<len2;i++) s[len1++i]=s1[i];
s[len1+len2+]='*';
s[len1+len2+]='\0';
n=strlen(s);
build_sa();
getHeight(n-); ll sum=,ans=; //sum代表栈里对答案的贡献值,cnt数组记录的是一个组内的后缀数
int top=;
for(int i=;i<n;i++) //处理A和它前面的B的公共前缀
{
if(height[i]<k) {top=;sum=;continue;}
int num=;
while(top && sta[top]>height[i])
{
sum-=(ll)(sta[top]-k+)*cnt[top]; //在这儿比height[i]大的都要变成height[i],所以先减去先前加上去的
sum+=(ll)(height[i]-k+)*cnt[top]; //乘cnt[top]的原因是可能有多个合并成一个了,比如4,5,3,那么4和5都只能以3来计算
num+=cnt[top]; //那么此时这三个可以合并在一起,cnt个数就是3,和就是3*3=9
top--;
}
sta[++top]=height[i];
if(sa[i-]>len1)
{
sum+=(ll)height[i]-k+;
cnt[top]=num+;
}
else cnt[top]=num;
if(sa[i]<len1)
ans+=sum;
} sum=,top=;
for(int i=;i<n;i++) //处理B和它前面的A的公共前缀
{
if(height[i]<k) {top=;sum=;continue;}
int num=;
while(top && sta[top]>height[i])
{
sum-=(ll)(sta[top]-k+)*cnt[top];
sum+=(ll)(height[i]-k+)*cnt[top];
num+=cnt[top];
top--;
}
sta[++top]=height[i];
if(sa[i-]<len1)
{
sum+=(ll)height[i]-k+;
cnt[top]=num+;
}
else cnt[top]=num;
if(sa[i]>len1)
ans+=sum;
}
printf("%I64d\n" , ans);
}
return ;
}

POJ 3415 Common Substrings(长度不小于K的公共子串的个数+后缀数组+height数组分组思想+单调栈)的更多相关文章

  1. 【POJ 3415】Common Substrings 长度不小于k的公共子串的个数

    长度不小于k的公共子串的个数,论文里有题解,卡了一上午,因为sum没开long long!!! 没开long long毁一生again--- 以后应该早看POJ里的Discuss啊QAQ #inclu ...

  2. POJ 3415 Common Substrings 【长度不小于 K 的公共子串的个数】

    传送门:http://poj.org/problem?id=3415 题意:给定两个串,求长度不小于 k 的公共子串的个数 解题思路: 常用技巧,通过在中间添加特殊标记符连接两个串,把两个串的问题转换 ...

  3. Common Substrings POJ - 3415(长度不小于k的公共子串的个数)

    题意: 给定两个字符串A 和 B, 求长度不小于 k 的公共子串的个数(可以相同) 分两部分求和sa[i-1] > len1  sa[i] < len1  和  sa[i-1] < ...

  4. POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)

    Description A substring of a string T is defined as: T( i, k)= TiTi+1... Ti+k-1, 1≤ i≤ i+k-1≤| T|. G ...

  5. poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数

    Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11469   Accepted: 379 ...

  6. POJ-Common Substrings(后缀数组-长度不小于 k 的公共子串的个数)

    题意: 长度不小于 k 的公共子串的个数 分析: 基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于 k 的部分全部加起来. 先将两个字符串连起来,中间 ...

  7. POJ 3415 不小于k的公共子串的个数

    Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 9248   Accepted: 3071 ...

  8. POJ - 3415 Common Substrings (后缀数组)

    A substring of a string T is defined as: T( i, k)= TiTi +1... Ti+k -1, 1≤ i≤ i+k-1≤| T|. Given two s ...

  9. POJ 3415 Common Substrings(后缀数组 + 单调栈)题解

    题意: 给两个串\(A.B\),问你长度\(>=k\)的有几对公共子串 思路: 先想一个朴素算法: 把\(B\)接在\(A\)后面,然后去跑后缀数组,得到\(height\)数组,那么直接\(r ...

随机推荐

  1. SQL小结

    1. 一般而言,除非你确实需要表中的每一列,否则最好别使用*通配符.虽然使用通配符让你自己省事,不用明确列出所需列,但检索不需要的列通常会降低检索和应用程序的性能. 2. DISTINCT关键字作用于 ...

  2. 【JavaScript 6连载】一、关于对象(访问)

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  3. php获得可靠的精准的当前时间 ( 通过授时服务器 )

    有一种情形是这样子的,比如机票业务中的订票流程,我们需要一个非常可靠的当前时间来支持,尽管大多数服务器的时间是非常准确的,我们使用time()来获取的时间是可靠的,但未免会有不确切的情况,也有的服务器 ...

  4. ztree使用实例

    一.当某节点被选择或被取消选择时获取所有被选择的节点: <link rel="stylesheet" href="./static/libs/ztree/css/m ...

  5. bzoj1180 tree

    题目链接 link cut tree 模板题 link cut tree不都是模板题嘛?(雾 #include<algorithm> #include<iostream> #i ...

  6. python--字典dict

    字典由多个键与其对应的值构成的对组成,是另一种可变容器模型,且可存储任意类型对象.字典的每个键值用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中. 注:字典中的键是唯一的( ...

  7. NoSql Cassandra

    我们为什么要使用NOSQL非关系数据库? 随着互联网web2.0网站的兴起,非关系型的数据库现在成了一个极其热门的新领域,非关系数据库产品的发展非常迅速.而传统的关系数据库在应付web2.0网站,特别 ...

  8. input 的radio checkbox 和 select 相关操作

    1  select 获取和设置值,以及onchange事件 1下拉框option没有checked事件 可通过select 的 onchange事件进行监控,以获取其值 <select name ...

  9. oracle goldengate 远程捕获和投递

    很早之前,OGG只支持部署在数据库主机上,这叫本地化部署.而现在OGG支持远端部署,即OGG软件不安装在数据库主机上,而是安装在单独的机器上,负责数据抽取和投递. 这样做的好处: l 易于管理 - 在 ...

  10. HTML(续)

    1.有frame就无body,框架的noresize:设置框架大小不能改变.2.链接在框架中的应用target:有定为目标的功能.<a href = "链接源地址" targ ...