[HAOI2016] 找相同字符

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。 \(n,m \le 200000\)

Solution

将两个字符串按序连接,中间用一个其它字符隔开,新串记为 \(S\) ,那么 \(S[1 , n]\) 为第一个字符串的对应部分, \(S[n+2 ,n+m+1]\) 为第二个字符串对应的部分。预处理出 \(S\) 的后缀数组和高度数组。

我们将这些后缀中起始位置在 \([1,n]\) 内的称为黑后缀, \([n+2,n+m+1]\) 内的成为白后缀。那么我们可以考虑对每个白后缀它与所有黑后缀匹配的答案,这个贡献就是它与所有黑后缀的 \(LCP\) 长度的和。

假设所有后缀的顺序是按后缀排序的,不难想到分为左边的黑后缀和右边的黑后缀两部分,分开处理。

对于所黑后缀在白后缀左边的答案,我们可以按照顺序扫描所有的白后缀,同时维护到当前位置为止,当前位置串与前面任意一个位置串的 \(LCP\) 长度,同时记录它们的和。不难发现这可以用一个单调栈来处理。具体地,我们维护一个单调递增的栈,在栈中需要记录每个元素的下标,配合一个描述黑白后缀的前缀和数组,这样可以快速更新答案。

每当我们扫描到一个白后缀,就把单调栈中的和加进答案。

同理对所有黑后缀在白后缀右边的答案也去这样处理一遍。得到的就是最终的结果。

实现层面上这个题还是挺水了(虽然还是挠了半天毛)

Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 500005;
#define reset(x) memset(x,0,sizeof x)
namespace SA
{
int n,m=256,sa[N],y[N],u[N],v[N],o[N],r[N],h[N],T;
char str[N];
void solve()
{
n=strlen(str+1);
for(int i=1; i<=n; i++)
u[str[i]]++;
for(int i=1; i<=m; i++)
u[i]+=u[i-1];
for(int i=n; i>=1; i--)
sa[u[str[i]]--]=i;
r[sa[1]]=1;
for(int i=2; i<=n; i++)
r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
for(int l=1; r[sa[n]]<n; l<<=1)
{
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memcpy(o,r,sizeof r);
for(int i=1; i<=n; i++)
u[r[i]]++, v[r[i+l]]++;
for(int i=1; i<=n; i++)
u[i]+=u[i-1], v[i]+=v[i-1];
for(int i=n; i>=1; i--)
y[v[r[i+l]]--]=i;
for(int i=n; i>=1; i--)
sa[u[r[y[i]]]--]=y[i];
r[sa[1]]=1;
for(int i=2; i<=n; i++)
r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
}
{
int i,j,k=0;
for(int i=1; i<=n; h[r[i++]]=k)
for(k?k--:0,j=sa[r[i]-1]; str[i+k]==str[j+k]; k++);
}
}
} namespace MS
{
int a[N],b[N],c[N],p,ans;
void init()
{
reset(a);
reset(b);
reset(c);
p=0;
ans=0;
}
void push(int x,int y)
{
while(x<=a[p]&&p)
{
ans-=(c[b[p]]-c[b[p-1]])*a[p];
--p;
}
a[++p]=x;
b[p]=y;
ans+=(c[b[p]]-c[b[p-1]])*x;
}
} char a[N],b[N];
int n,m,t1,t2,t3,t4,ans; signed main()
{
cin>>a+1>>b+1;
n=strlen(a+1);
m=strlen(b+1);
reset(SA::str);
for(int i=1; i<=n; i++)
SA::str[i]=a[i];
SA::str[n+1]='$';
for(int i=1; i<=m; i++)
SA::str[n+i+1]=b[i];
SA::solve();
for(int i=1; i<=n+m+1; i++)
MS::c[i+1]=(SA::sa[i]<=n?1:0);
for(int i=1; i<=n+m+1; i++)
MS::c[i]+=MS::c[i-1];
for(int i=1; i<=n+m+1; i++)
{
MS::push(SA::h[i],i);
if(SA::sa[i]<=n+1)
continue;
ans+=MS::ans;
}
MS::init();
for(int i=1; i<=n+m+1; i++)
MS::c[i+1]=(SA::sa[i]>n+1?1:0);
for(int i=1; i<=n+m+1; i++)
MS::c[i]+=MS::c[i-1];
for(int i=1; i<=n+m+1; i++)
{
MS::push(SA::h[i],i);
if(SA::sa[i]>n)
continue;
ans+=MS::ans;
}
cout<<ans<<endl;
}

[HAOI2016] 找相同字符 - 后缀数组,单调栈的更多相关文章

  1. 【BZOJ4566】[Haoi2016]找相同字符 后缀数组+单调栈

    [BZOJ4566][Haoi2016]找相同字符 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同 ...

  2. BZOJ.4566.[HAOI2016]找相同字符(后缀数组 单调栈)

    题目链接 给定两个字符串,求它们有多少个相同子串.相同串的位置不同算多个. POJ3145简化版. 后缀自动机做法见这儿,又快又好写(一下就看出差距了..) //13712kb 4076ms #inc ...

  3. 【BZOJ-3238】差异 后缀数组 + 单调栈

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1561  Solved: 734[Submit][Status] ...

  4. BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈

    BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao ...

  5. BZOJ_3879_SvT_后缀数组+单调栈

    BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...

  6. BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)

    BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...

  7. 【BZOJ3879】SvT 后缀数组+单调栈

    [BZOJ3879]SvT Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干 ...

  8. BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】

    题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...

  9. BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】

    题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...

随机推荐

  1. nvm Nodejs 版本管理器 安装及配置

    1.如果已安装nodejs请先卸载干净 nodejs:删除C:\Program Files\nodejs 文件夹 npm:删除C:\Users\{用户名}\AppData\Roaming\npm 文件 ...

  2. CF594D REQ [离线+树状数组,欧拉函数]

    设 \[x = \prod_{i=1}^{cnt} p_i^{k_i} [p_i\in prime]\] 那么显然 \[\varphi(x) = x*\frac{1} {\prod_{i=1}^{cn ...

  3. SpringBoot初学(4)– JdbcTemplate和Mybatis

    前言 github: https://github.com/vergilyn/SpringBootDemo 代码位置: 一.Spring Boot集成JdbcTemplate或NamedParamet ...

  4. CF571D Campus(19-1)

    题意 \(n\)个点,维护两个森林,这里\(A,B\)两个森林对应的点都是一样的,相当于对两个森林操作都会影响这\(n\)个点 开始森林里的树都是一个点,\(A,B\)支持合并(但树结构互不影响),\ ...

  5. PAT (Basic Level) Practice (中文)1021 个位数统计 (15 分)

    给定一个 k 位整数 1 (0, ,, d​k−1​​>0),请编写程序统计每种不同的个位数字出现的次数.例如:给定 0,则有 2 个 0,3 个 1,和 1 个 3. 输入格式: 每个输入包含 ...

  6. js面向对象怎么理解

    js面向对象怎么理解 <一>. 认识对象.首先要认识对象:在编程中,对象(object)是具体的某一个实例,唯一的某一个个体.如:电脑就是一个统称,而你面前的这一台电脑就是对象.而电脑的统 ...

  7. win10专业工作站版密钥|win10专业工作站版key|win10专业工作站版激活码

    一.windows10专业工作站零售版密钥 永久激活 Win 10 ProfessionalWorkstation Retail [Key]:DXG7C-N36C4-C4HTG-X4T3X-2YV77 ...

  8. 02:QT的第一个程序

    新建项目,有这么几个文件: main.cpp                      //一个main函数,作为应用程序的入口函数 mainwindow.cpp mainwindow.h untit ...

  9. CentOS MySQL自动备份shell脚本

    先执行  vim/mysqlBack/back.sh 然后添加以下内容 ## 记录日志 # 以下配置信息请自己修改 mysql_user="root" #MySQL备份用户 mys ...

  10. from selenium.webdriver.chrome.options import Options中add_argument 常用参数表收集

    chrome_options.add_argument("xxx") 序号 参数 说明 1 --allow-outdated-plugins 不停用过期的插件. 2 --allow ...