题意:  给出两个串,问这两个串的所有的子串中(重复出现的,只要是位置不同就算两个子串),长度大于等于k的公共子串有多少个。

题解:

这题好像大神们都用后缀数组做。。然而我在sam的题表上看到这题,做了一百年才做出来。。还看了题解好吗。。

先对第一个串构造 SAM,逆拓扑序求出right存入r[]。

某个节点的right集合表示Min(x)~Max(x)这一段子串都出现了r[x]次

用第二个串对 SAM 做 LCS,当前节点x LCS>=K时,ans+=ans+=r[x]*(len-maxx(k,step[pre[x]]+1)+1);(当前匹配的最长串的子串数)。

如果step[pre[x]]>=k,cnt[pre[x]]++;

比如样例:(k=1)

xx

xx

我给它们编号

x1 x2

x3 x4

那么跑串2的x3的时候跑到了sam上的x1节点,ans++;

但是x2也可以跟x3匹配呀

跑到x4的时候,sam跑到了x2

我们可以在x2的pre也就是x1上打个标记,以后加上去。

用字符串A构造SAM,在SAM上匹配第二个字符串B,设当前匹配长度为len,且位于状态p,则当前状态中满足条件长度不小于K的公共子串的字符串个数为

    sum = len-max{ K,Min(p) }+1

  SAM中一个状态代表的字符串长度为一个连续区间[ Min(s),Max(s) ],Min(s)为最小长度。

  这些字符串重复的次数为|right|,即right集的大小,可以递推得到,则当前状态对于答案的贡献为sum*|right|

  这时候匹配的是p,还应该统计parent树中p->root的路径上的状态中满足条件的个数。

  这里设一个懒标记tag[x],记录节点x需要统计的次数,最后算一遍,每次如果Max(p->fa) >= K则上传标记。

  需要注意的是可能出现大写字符 =_=

  相比较而言SAM的做法更好想一些。

---------------------------------------------------------------------http://www.cnblogs.com/lidaxin/p/5005079.html

拓扑序逆序统计不是最长公共子串的状态但是被子串包含的个数,ans+=cnt[p]*(step[p]- max(K,Min(p)+1)*r[p],同时维护cnt:cnt[pre[p]]+=cnt[p]。

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<ctime>
#include<algorithm>
using namespace std; typedef long long LL;
const int N=*,S=;
int k,tot,last,al,bl,cl;
int son[N][],pre[N],step[N],in[N],c[N],r[N],cnt[N];
char a[N],b[N];
bool vis[N];
queue<int> Q; int minn(int x,int y){return x<y ? x:y;}
int maxx(int x,int y){return x>y ? x:y;} int idx(char ch)
{
if(ch>='a' && ch<='z') return ch-'a'+;
return ch-'A'++;
} int add_node(int x)
{
step[++tot]=x;
return tot;
} void clear()
{
memset(r,,sizeof(r));
memset(cnt,,sizeof(cnt));
memset(son,,sizeof(son));
memset(pre,,sizeof(pre));
memset(step,,sizeof(step));
tot=;add_node();last=;
} void extend(int ch)
{
int p=last,np=add_node(step[last]+);
while(p && !son[p][ch]) son[p][ch]=np,in[np]++,p=pre[p];
if(!p) pre[np]=;
else
{
int q=son[p][ch];
if(step[q]==step[p]+) pre[np]=q;
else
{
int nq=add_node(step[p]+);
memcpy(son[nq],son[q],sizeof(son[q]));
for(int i=;i<=;i++)
if(son[q][i]) in[son[q][i]]++;
pre[nq]=pre[q];
pre[np]=pre[q]=nq;
while(son[p][ch]==q) in[q]--,in[nq]++,son[p][ch]=nq,p=pre[p];
}
}
last=np;
} void find_tp()
{
while(!Q.empty()) Q.pop();
Q.push();vis[]=;cl=;
while(!Q.empty())
{
int x=Q.front();vis[x]=;c[++cl]=x;Q.pop();
for(int i=;i<=;i++)
{
int y=son[x][i];
if(!y) continue;
in[y]--;
if(!in[y] && !vis[y]) vis[y]=,Q.push(y);
}
}
} void find_right()
{
int x=,ch;
for(int i=;i<=al;i++)
{
ch=idx(a[i]);
x=son[x][ch];
r[x]++;
}
for(int i=cl;i>=;i--) r[pre[c[i]]]+=r[c[i]];
} int main()
{
freopen("a.in","r",stdin);
int x,ch,len,ans;
while()
{
scanf("%d",&k);
if(!k) return ;
scanf("%s%s",a+,b+);
al=strlen(a+);
bl=strlen(b+);
clear();
for(int i=;i<=al;i++) extend(idx(a[i]));
find_tp();
find_right();
// for(int i=1;i<=cl;i++) printf("%d ",c[i]);printf("\n");
// for(int i=1;i<=tot;i++) printf("r %d = %d\n",i,r[i]);
x=,len=;
LL ans=;
for(int i=;i<=bl;i++)
{
ch=b[i]-'a'+;
while(x && !son[x][ch]) x=pre[x],len=step[x];
x=son[x][ch];len++;
if(x==) x=,len=;
if(len>=k)
{
cnt[x]++;
ans+=r[x]*(len-maxx(k,step[pre[x]]+)+);
}
}
for(int i=cl;i>=;i--) cnt[pre[c[i]]]+=cnt[c[i]];
for(int i=;i<=tot;i++)
{
int fa=pre[i];
if(!fa) continue;
if(step[fa]>=k) ans+=cnt[i]*(step[fa]-maxx(k,step[pre[fa]])+)*r[fa];
}
printf("%lld\n",ans);
}
return ;
}

【poj3415-Common Substrings】sam子串计数的更多相关文章

  1. POJ3415 Common Substrings —— 后缀数组 + 单调栈 公共子串个数

    题目链接:https://vjudge.net/problem/POJ-3415 Common Substrings Time Limit: 5000MS   Memory Limit: 65536K ...

  2. poj3415 Common Substrings(后缀数组,单调栈 | 后缀自动机)

    [题目链接] http://poj.org/problem?id=3415 [题意] A与B长度至少为k的公共子串个数. [思路] 基本思想是将AB各个后缀的lcp-k+1的值求和.首先将两个字符串拼 ...

  3. 2018.12.15 poj3415 Common Substrings(后缀自动机)

    传送门 后缀自动机基础题. 给两个字符串,让你求长度不小于kkk的公共子串的数量. 这题可以用后缀自动机解决废话 考虑对其中一个字串建出后缀自动机,然后用另一个在上面跑,注意到如果一个状态有贡献的话, ...

  4. POJ3415 Common Substrings(后缀数组 单调栈)

    借用罗穗骞论文中的讲解: 计算A 的所有后缀和B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k 的部分全部加起来.先将两个字符串连起来,中间用一个没有出现过的字符隔开.按height ...

  5. POJ3415 Common Substrings

    后缀数组 求长度不小于k的公共子串的个数 代码: #include <stdio.h> #include <string.h> ; int len, len1; int wa[ ...

  6. POJ3415 Common Substrings 【后缀数组 + 单调栈】

    常见的子串 时间限制: 5000MS   内存限制: 65536K 提交总数: 11942   接受: 4051 描述 字符串T的子字符串被定义为: Ť(我,ķ)= Ť 我 Ť 我 1 ... Ť I ...

  7. poj3415 Common Substrings (后缀数组+单调队列)

    Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 9414   Accepted: 3123 Description A sub ...

  8. 【POJ3415】 Common Substrings(后缀数组|SAM)

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

  9. Common Substrings POJ - 3415 (后缀自动机)

    Common Substrings \[ Time Limit: 5000 ms\quad Memory Limit: 65536 kB \] 题意 给出两个字符串,要求两个字符串公共子串长度不小于 ...

随机推荐

  1. Java Algorithm Problems

    Java Algorithm Problems 程序员的一天 从开始这个Github已经有将近两年时间, 很高兴这个repo可以帮到有需要的人. 我一直认为, 知识本身是无价的, 因此每逢闲暇, 我就 ...

  2. 九度OJ--Q1473

    import java.util.ArrayList;import java.util.Scanner; /* * 题目描述: * 大家都知道,数据在计算机里中存储是以二进制的形式存储的. * 有一天 ...

  3. Hyperledger02

    docker 思想 模块化: 集装箱 标准化: 运输标准化, 存储方式标准化,API接口的标准化 安全性: 隔离 docker解决什么问题 devops 我这程序程序没问题啊 系统好卡.哪个程序死循环 ...

  4. [leetcode-655-Print Binary Tree]

    Print a binary tree in an m*n 2D string array following these rules: The row number m should be equa ...

  5. Python之tornado框架实现翻页功能

    1.结果如图所示,这里将html页面与网站的请求处理放在不同地方了 start.py代码 import tornado.ioloop import tornado.web from controlle ...

  6. 给曾经是phper的程序员推荐个学习网站

    如果你原来是一个php程序员,你对于php函数非常了解(PS:站长原来就是一个php程序员),但是现在由于工作或者其他原因要学习python,但是python很多函数我们并不清楚,在这里我给大家推荐一 ...

  7. C++关于堆的函数

    建立堆 make_heap(_First, _Last, _Comp) 默认是建立最大堆的.对int类型,可以在第三个参数传入greater<int>()得到最小堆.   在堆中添加数据 ...

  8. 【bzoj2957】楼房重建 分块+二分查找

    题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子.为了简化问题,我们考虑这些事件发生在一个二 ...

  9. Luogu3953 NOIP2017逛公园(最短路+拓扑排序+动态规划)

    跑一遍dij根据最短路DAG进行拓扑排序,按拓扑序dp即可.wa了三发感觉非常凉. #include<iostream> #include<cstdio> #include&l ...

  10. hdu 1284 钱币兑换问题 (递推 || DP || 母函数)

    钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...