BZOJ4566 [Haoi2016]找相同字符 【后缀数组】
题目
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。
输入格式
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
输出格式
输出一个整数表示答案
输入样例
aabb
bbaa
输出样例
10
题解
先考虑暴力怎么做
我们枚举两个串的各自一个后缀suffix(i)和suffix(j)
则他们对答案的贡献是LCP(suffix(i),suffix(j))
如此得到一个\(O(n^3)\)的算法
当然如果你知道后缀数组,可以\(O(1)\)求LCP,可以优化到\(O(n^2)\)
当然如果你知道后缀数组的套路,用一个单调栈扫一遍height[]可以做到\(O(nlogn)\)【主要复杂度在求后缀数组】
具体这样:
我们知道,两个后缀之间的LCP是他们之间height的最小值
如果给出一个位置的后缀,想求这个位置之前所有的后缀与这个位置的LCP之和之类的东西,由于求最小值是一路过去的,所以前面的后缀的LCP不会比后面的大,所以整体是单调不下降的,可以用单调栈处理
最后我们只需要分A、B串各用单调栈扫两次统计出答案就可以了
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 400005,maxm = 100005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
return out * flag;
}
int sa[maxn],rank[maxn],height[maxn],t1[maxn],t2[maxn],bac[maxn],n,m;
char s[maxn];
int len;
LL ans;
void getSA(){
int *x = t1,*y = t2; m = 256;
for (int i = 0; i <= m; i++) bac[i] = 0;
for (int i = 1; i <= n; i++) bac[x[i] = s[i]]++;
for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
for (int i = n; i; i--) sa[bac[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1){
int p = 0;
for (int i = n - k + 1; i <= n; i++) y[++p] = i;
for (int i = 1; i <= n; i++) if (sa[i] - k > 0) y[++p] = sa[i] - k;
for (int i = 0; i <= m; i++) bac[i] = 0;
for (int i = 1; i <= n; i++) bac[x[y[i]]]++;
for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
for (int i = n; i; i--) sa[bac[x[y[i]]]--] = y[i];
swap(x,y);
p = x[sa[1]] = 1;
for (int i = 2; i <= n; i++)
x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p);
if (p >= n) break;
m = p;
}
for (int i = 1; i <= n; i++) rank[sa[i]] = i;
for (int i = 1,k = 0; i <= n; i++){
if (k) k--;
int j = sa[rank[i] - 1];
while (s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
}
int st[maxn],cnt[maxn],top;
LL cal[maxn];
void solve(){
for (int i = 1; i <= n; i++){
if (!height[i]) {top = 0; continue;}
int tot = sa[i - 1] <= len ? 1 : 0;
while (top && st[top] > height[i]) tot += cnt[top--];
if (tot) st[++top] = height[i],cnt[top] = tot;
cal[top] = cal[top - 1] + (LL)st[top] * (LL)cnt[top];
if (sa[i] > len) ans += cal[top];
}
top = 0;
for (int i = 1; i <= n; i++){
if (!height[i]) {top = 0; continue;}
int tot = sa[i - 1] > len ? 1 : 0;
while (top && st[top] > height[i]) tot += cnt[top--];
if (tot) st[++top] = height[i],cnt[top] = tot;
cal[top] = cal[top - 1] + (LL)st[top] * (LL)cnt[top];
if (sa[i] <= len) ans += cal[top];
}
}
int main(){
scanf("%s",s + 1); len = strlen(s + 1);
s[len + 1] = '#';
scanf("%s",s + len + 2);
n = strlen(s + 1);
getSA();
solve();
cout << ans << endl;
return 0;
}
BZOJ4566 [Haoi2016]找相同字符 【后缀数组】的更多相关文章
- 【BZOJ4566】[Haoi2016]找相同字符 后缀数组+单调栈
[BZOJ4566][Haoi2016]找相同字符 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同 ...
- [HAOI2016] 找相同字符 - 后缀数组,单调栈
[HAOI2016] 找相同字符 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. \(n,m \l ...
- [BZOJ4566][Haoi2016]找相同字符 后缀自动机+dp
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1212 Solved: 694[Submit][Stat ...
- BZOJ.4566.[HAOI2016]找相同字符(后缀数组 单调栈)
题目链接 给定两个字符串,求它们有多少个相同子串.相同串的位置不同算多个. POJ3145简化版. 后缀自动机做法见这儿,又快又好写(一下就看出差距了..) //13712kb 4076ms #inc ...
- BZOJ4566: [Haoi2016]找相同字符(后缀自动机)
题意 题目链接 Sol 直接在SAM上乱搞 枚举前缀,用SAM统计可以匹配的后缀,具体在匹配的时候维护和当前节点能匹配的最大值 然后再把parent树上的点的贡献也统计上,这部分可以爆跳parent树 ...
- BZOJ4566 [Haoi2016]找相同字符【SAM】
BZOJ4566 [Haoi2016]找相同字符 给定两个字符串\(s和t\),要求找出两个字符串中所有可以相互匹配的子串对的数量 首先考虑可以怎么做,我们可以枚举\(t\)串的前缀\(t'\),然后 ...
- [Bzoj4566][Haoi2016]找相同字符(广义后缀自动机)
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 861 Solved: 495[Submit][Statu ...
- BZOJ 4566: [Haoi2016]找相同字符 [后缀自动机]
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 275 Solved: 155[Submit][Statu ...
- BZOJ4566 Haoi2016 找相同字符【广义后缀自动机】
Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. Input 两行,两个字符串s1,s2,长度分别 ...
随机推荐
- python基础一 day15 复习
迭代器和生成器迭代器 可迭代协议 —— 含有iter方法的都是可迭代的 迭代器协议 —— 含有next和iter的都是迭代器 特点 节省内存空间 方便逐个取值,一个迭代器只能取一次.生成器 —— 迭代 ...
- python_85_sys模块
import sys print(sys.version)#当前python版本的详细信息 print(sys.argv)#脚本中运行,读取参数
- C++类和结构体的区别
C++类和结构体的区别? 结构体默认数据访问控制是public; 类默认数据访问控制是private;
- ovx openVirtex安装
搞了好久的pox实验,中途一个星期没更新了吧, 今天继续... 新手第一次搞还是在虚拟机上最安全~ ovx参照上面的做吧,注意必须在联网的情况下,否则很多控件都连不上(第一次做的时候虚拟机没配ip 坑 ...
- cocos2dx 通过jni调用安卓底层方法
cocos2dx通过封装JniHelper类来调用安卓api底层函数,该文件在cocos/platform/android/jni/JniHelper.h,使用方法如下: 打开eclipse,导入co ...
- iOS项目工程及目录结构
做过一些iOS的项目,不同项目的沉淀没有积累到一起,目录的管理都在后期随着人员的增加越来越混乱,因此在这里做一些梳理,希望达到两个目的. 一套相对通用的目录结构,作为后续项目的模版. 积累相应的基础库 ...
- React组件自适应窗口宽高
很多时候我们需要组件能够根据窗口变化改变宽高,有时候可以使用css,有时候需要随数据调整则使用js计算. 比如说,当我们在页面中放置一个iframe时,我们希望它的宽高随着其父元素or窗口的变化而变化 ...
- numpy的linspace使用详解
文档地址: https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html Parameters(参数): start ...
- 洛谷 P3601 签到题
https://www.luogu.org/problemnew/show/P3601 一道关于欧拉函数的题. 读完题目以后我们知道所谓的$aindao(x)=x- \phi (x) $. 对于x小的 ...
- MySQL数据库的多种备份与多种还原
一.备份 1.mysqldump 方法备份 mysqldump备份很简单,格式如下: mysqldump -u用户名 -p密码 数据库名> XX.sql 路径 例如: mysqldump -ur ...