poj3415

题意

给定两个字符串,给出长度 \(m\) ,问这两个字符串有多少对长度大于等于 \(m\) 且完全相同的子串。

分析

首先连接两个字符串 A B,中间用一个特殊符号分割开。

按照 \(sa\) 的顺序(即枚举 \(height\) 值),进行分组,那么有公共前缀长大于等于 \(m\) 的都分到了一组,对于某一组,后缀串可能来自于 A 也可能来自于 B,那么对于 A 找前面的 B 串,对于 B 找前面的 A 串,如果某两个后缀串的公共前缀长为 \(l(l \geqslant m)\),那么显然会有 \(l - m + 1\) 对子串。

注意到这个性质: 对于两个后缀串 j 和 k,设 \(rnk[j] < rnk[k]\) ,LCP长度为 \(height[rnk[j]+1], height[rnk[j]+2], ... , height[rnk[k]]\) 中的最小值。

维护一个单调递增的栈(保证栈顶最大)可以用一个二维数组表示(\(q[][2]\)),一个是栈,一个是某个数的个数。

举个例子,如果连续的 \(height\) 值为 \(2 \ 3 \ 4\) ,\(m = 2\),前三个为 A 串,那么 \(2 \ 3 \ 4\) 全部入栈,且计算对答案的贡献 \(sum\)(不是直接加到答案上),即 \((2-2+1) + (3-2+1) + (4-2+1)\) ,到 B 串时,答案就加上了这个值,但是如果后面还有一个 B 串且 \(height\) 为 \(3\),那么就要弹栈,且减去 \(sum\) 值多的那部分(前面多算了),栈里 \(4\) 的数量为 \(1\),所以 \(sum = sum - (4 - 3) * 1\) ,且栈里 \(3\) 的数量变为了 \(2\) ( \(4\) 对应的 A 串对于后面串提供的贡献减小了(注意前面的性质),所以\(4\) 变为了 \(3\) ),答案加上 \(sum\)。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 10;
const int INF = 1e9;
char s[MAXN];
int sa[MAXN], t[MAXN], t2[MAXN], c[MAXN], n; // n 为 字符串长度 + 1,即最后一位为数字 0
int rnk[MAXN], height[MAXN];
// 构造字符串 s 的后缀数组。每个字符值必须为 0 ~ m-1
void build_sa(int m) {
int i, *x = t, *y = t2;
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(int k = 1; k <= n; k <<= 1) {
int p = 0;
for(i = n - k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 0; i < m; i++) c[i] += c[i - 1];
for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
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] + k] == y[sa[i] + k] ? p - 1 : p++;
if(p >= n) break;
m = p;
}
}
void getHeight() {
int i, j, k = 0;
for(i = 0; i < n; i++) rnk[sa[i]] = i;
for(i = 0; i < n - 1; i++) {
if(k) k--;
j = sa[rnk[i] - 1];
while(s[i + k] == s[j + k]) k++;
height[rnk[i]] = k;
}
}
char s2[MAXN];
int q[MAXN][2];
int main() {
int m;
while(~scanf("%d", &m) && m) {
scanf("%s%s", s, s2); // A 、B串
int l = strlen(s), l2 = strlen(s2);
s[l++] = '#';
for(int i = 0; i < l2; i++) s[i + l] = s2[i];
s[l + l2] = 0;
n = l + l2 + 1;
build_sa(128);
getHeight();
ll ans = 0, sum = 0;
int top = 0;
// 在 B 串前找 A
for(int i = 2; i < n; i++) {
int cnt = 0;
if(height[i] < m) {
top = 0; sum = 0;
continue;
}
if(sa[i - 1] < l) {
cnt++;
sum += height[i] - m + 1;
}
while(top && q[top - 1][0] >= height[i]) {
top--;
sum -= (q[top][0] - height[i]) * q[top][1];
cnt += q[top][1];
}
q[top][0] = height[i]; q[top++][1] = cnt;
if(sa[i] >= l) ans += sum;
}
// 在 A 串前找 B
sum = 0; top = 0;
for(int i = 2; i < n; i++) {
int cnt = 0;
if(height[i] < m) {
top = 0; sum = 0;
continue;
}
if(sa[i - 1] >= l) {
cnt++;
sum += height[i] - m + 1;
}
while(top && q[top - 1][0] >= height[i]) {
top--;
sum -= (q[top][0] - height[i]) * q[top][1];
cnt += q[top][1];
}
q[top][0] = height[i]; q[top++][1] = cnt;
if(sa[i] < l) ans += sum;
}
printf("%lld\n", ans);
}
return 0;
}

poj3415(后缀数组)的更多相关文章

  1. 【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≤ ...

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

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

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

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

  4. 关于后缀数组的倍增算法和height数组

    自己看着大牛的论文学了一下后缀数组,看了好久好久,想了好久好久才懂了一点点皮毛TAT 然后就去刷传说中的后缀数组神题,poj3693是进化版的,需要那个相同情况下字典序最小,搞这个搞了超久的说. 先简 ...

  5. 【距离GDOI:131天】 后缀数组完毕

    用了近两周的时间,终于把罗神那篇后缀数组应用看完了,题目也写了一遍,T了无数次...详见前几篇博文... 后缀数组很重要的是那个height数组,可以用来做各种奇奇怪怪的东西...常用方法去是去二分, ...

  6. 后缀数组的倍增算法(Prefix Doubling)

    后缀数组的倍增算法(Prefix Doubling) 文本内容除特殊注明外,均在知识共享署名-非商业性使用-相同方式共享 3.0协议下提供,附加条款亦可能应用. 最近在自学习BWT算法(Burrows ...

  7. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

  8. BZOJ 1692: [Usaco2007 Dec]队列变换 [后缀数组 贪心]

    1692: [Usaco2007 Dec]队列变换 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1383  Solved: 582[Submit][St ...

  9. POJ3693 Maximum repetition substring [后缀数组 ST表]

    Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9458   Acc ...

随机推荐

  1. day06_02 元组

    1.0 元组 元组被称为只读列表,即数据可以被查询,但不能被修改,所以,列表的切片操作同样适用于元组.元素卸载小括号(())里,元素之间用逗号隔开. tup1 = () #空元组 tup2 = (20 ...

  2. Asp.net获取网站绝对路径的几种方法

    在编写ASP.NET应用程序的时候,有时候为了更好的进行控制静态文件的路径,以及网站部署过程中的虚拟路径等问题,采用绝对路径避免资源出现Not Found,下面先看看几种获取绝对路径的方法: 1. 以 ...

  3. eclipse importing maven projects 卡顿

    导入一个maven工程后 一直显示 importing maven projects 9% 解决办法: 找到eclipse安装目录下的eclipse.ini 在最后加入 -vm $JAVA_HOME% ...

  4. Python全栈工程师(for、列表)

    ParisGabriel     Python 入门基础         for:用来遍历可迭代对象的数据元素可迭代对象是指以此获取数据元素的对象可迭代对象包括:字符串 str 列表 list元组 t ...

  5. COMMIT和ROLLBACK的用法

    从功能上划分,SQL语言可以分为DDL,DML和DCL三大类. 1.DDL(Data Definition Language)  数据定义语言,用于定义和管理 SQL 数据库中的所有对象的语言 : C ...

  6. c#用UpdatePanel实现接局部刷新

    通常我们看到局部刷新就会想到Ajax,但是我今天要说的是c#的一个控件,只要把服务器按钮和要刷新的区域放在该控件内就能实现局部刷新. 当然它必须和ScriptManager控件一起使用. Update ...

  7. Python中的返回函数与闭包

    返回函数,顾名思义,就是高阶函数可以把函数作为return值返回.与闭包的关系是:闭包需要以返回函数的形式实现. 一. 返回函数 比如我们有一个求和函数: >>> def calc_ ...

  8. 【bzoj2038】[2009国家集训队]小Z的袜子(hose) 莫队算法

    原文地址:http://www.cnblogs.com/GXZlegend/p/6803860.html 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终 ...

  9. 【bzoj3669】[Noi2014]魔法森林 Kruskal+LCT

    原文地址:http://www.cnblogs.com/GXZlegend/p/6797748.html 题目描述 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看 ...

  10. UVA1316 Supermarket

    题目描述 有一个商店有许多批货,每一批货又有N(0<=N<= 10^4104 )个商品,同时每一样商品都有收益 P_iPi​ ,和过期时间 D_iDi​ (1<= Pi,DiPi,D ...