题意:  给出两个串,问这两个串的所有的子串中(重复出现的,只要是位置不同就算两个子串),长度大于等于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. React+DvaJS 之 hook 路由权限控制

    博客 学院 下载 GitChat TinyMind 论坛 APP 问答 商城 VIP 活动 招聘 ITeye 写博客 发Chat 登录注册 原 React+DvaJS 之 hook 路由权限控制 20 ...

  2. JAVA虚拟机(一):内存区域

    根据<java虚拟机规范第二版>规定,现阶段的java内存区域总体如下图 其中,方法区和堆是所有线程共享区域. 虚拟机栈,本地方法栈,程序计数器是各线程独占. 概述一下各个区域 先说说线程 ...

  3. MySQL训练营03

    [任务四] #任务时间# 请于4月6日22:00前完成,在[打卡表格]处打卡.逾期尚未打卡的会被清退. 4.1 MySQL 实战 #学习内容# 数据导入导出 将之前创建的任意一张MySQL表导出,且是 ...

  4. parity的使用

    parity --chain dev --port 8045 ps aux | grep "parity" ps -elf | grep "pari"

  5. Python之tornado框架原理

    Python web框架 1.简单概念 tornado socket.逻辑处理 Django flask 逻辑处理 第三方处理模块(包含了socket) jinja2模块 Models 数据库处理 V ...

  6. [boost-2] 智能指针

    boost库学习: 智能指针: shared_ptr指针,定义在boost::shared_ptr,如果开发环境支持的话,可以使用memory中的std::shared_ptr. shared_ptr ...

  7. lintcode-106-排序列表转换为二分查找树

    106-排序列表转换为二分查找树 给出一个所有元素以升序排序的单链表,将它转换成一棵高度平衡的二分查找树 样例 标签 递归 链表 思路 类似于二分查找,每次将链表二分,中间节点作为根节点,在建立左子树 ...

  8. WEBSTORM中html文件运行之后出现乱码的问题解决

    出现如下问题: 解决方案: 1.点击"文件编码" 2.选择GBK 3.点击Reload. 4.此时,源代码中的中文字体会变成乱码,把这些乱码重新输入成原先的中文.然后运行html文 ...

  9. CSS设计指南之伪类

    伪类这个叫法源自它们与类相似,但实际上并没有类会附加到标记中的标签上.伪类分两种. UI伪类会在HTML元素处于某个状态时(比如鼠标指针位于链接上),为该元素应用CSS样式. 结构化伪类会在标记中存在 ...

  10. jdbc连接oracle语法

    public class LangDemo { public static void main(String[] args) throws Exception{ try { //加载驱动 Class. ...