【loj2064】找相同字符
Solution
这里是用后缀数组做的版本!(晚点再用Sam写一遍qwq)
首先一个字符串的子串其实就是这个字符串某个后缀的前缀,所以我们有一个十分简单粗暴的想法直接把两个串接起来(常用套路:中间加一个最大的字符作为分隔符)然后求Sa,求完之后我们就可以十分愉快地获得一个\(O(n^2)\)的算法了(每次枚举两个后缀,然后经过预处理之后我们可以用ST表\(O(1)\)求得lcp,答案显然就是这堆lcp之和)
然而\(O(n^2)\)十分不优秀,这里我们可以反过来想每一个\(height[i]\)可以在哪一个范围内作为最小值,具体的话我们可以用递归来实现
为了方便表示,我们记\(cnt1[i]\)表示排名前\(i\)的后缀中,属于第一个串的后缀数量,\(cnt2[i]\)表示排名前\(i\)的后缀中,属于第二个串的后缀数量
记\(solve(l,r)\)表示处理\(rk\)值\(\in [l,r]\)的这堆后缀对答案的贡献,我们可以用ST表求的这个区间内的\(height\)最小值为\(x\),然后我们记这个最小值的位置为\(mid\),那么这个区间内长度为\(x\)的取法对答案的贡献就是
x&*((cnt1[mid-1]-cnt1[l-1])*(cnt2[r]-cnt2[mid-1])+(cnt2[mid-1]-cnt2[l-1])*(cnt1[r]-cnt1[mid-1]))
\end{aligned}
\]
然后接着我们直接递归处理\([l,mid-1]\)区间和\([mid,r]\)区间就好了(其实好像区间这个加减的东西可以自己调整一下。。不过相对应的上面的式子中\(cnt1\)和\(cnt2\)的范围也需要稍微调整一下,都是具体实现个人习惯的问题)
注意答案可能比较大所以需要用long long
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define Pr pair<int,int>
#define mp make_pair
using namespace std;
const int N=200010,SA=N*2,TOP=20;
char s1[N],s2[N],s[N*2];
int n1,n2,n;
ll ans;
namespace Sa{/*{{{*/
int a[SA],b[SA],c[SA],sa[SA],rk[SA],height[SA];
int mn[SA][TOP+1],loc[SA][TOP+1];
int cnt1[SA],cnt2[SA];
int mx;
bool cmp(int x,int y,int len,int *r)
{return r[x]==r[y]&&r[x+len]==r[y+len];}
void sort(int n){
for (int i=0;i<=mx;++i) c[i]=0;
for (int i=1;i<=n;++i) ++c[a[b[i]]];
for (int i=1;i<=mx;++i) c[i]+=c[i-1];
for (int i=n;i>=1;--i) sa[c[a[b[i]]]--]=b[i];
}
void get_sa(int n){
mx=0;
for (int i=1;i<=n;++i) a[i]=s[i]-'a'+1,mx=max(mx,a[i]),b[i]=i;
sort(n);
int cnt=0;
for (int len=1;cnt<n;len<<=1){
cnt=0;
for (int i=n-len+1;i<=n;++i) b[++cnt]=i;
for (int i=1;i<=n;++i)
if (sa[i]>len)
b[++cnt]=sa[i]-len;
sort(n);
swap(a,b);
cnt=1; a[sa[1]]=1;
for (int i=2;i<=n;a[sa[i++]]=cnt)
if (!cmp(sa[i],sa[i-1],len,b)) ++cnt;
mx=cnt;
}
}
void rmq(int n){
for (int i=1;i<=n;++i) mn[i][0]=height[i],loc[i][0]=i;
for (int j=1;(1<<j)<=n;++j)
for (int i=n-(1<<j)+1;i>=1;--i){
if (mn[i][j-1]<mn[i+(1<<j-1)][j-1])
mn[i][j]=mn[i][j-1],loc[i][j]=loc[i][j-1];
else
mn[i][j]=mn[i+(1<<j-1)][j-1],loc[i][j]=loc[i+(1<<j-1)][j-1];
}
}
void get_height(int n){
for (int i=1;i<=n;++i) rk[sa[i]]=i;
int k=0;
for (int i=1;i<=n;++i){
if (k) --k;
while (s[i+k]==s[sa[rk[i]-1]+k]) ++k;
height[rk[i]]=k;
}
rmq(n);
}
Pr Lcp(int x,int y){//x y are ranks
if (x==y) return mp(n-sa[x]+1,x);
++x;
if (x>y) swap(x,y);
int len=y-x+1,lg=(int)(log(1.0*len)/log(2.0));
if (mn[x][lg]<mn[y-(1<<lg)+1][lg]) return mp(mn[x][lg],loc[x][lg]);
return mp(mn[y-(1<<lg)+1][lg],loc[y-(1<<lg)+1][lg]);
}
void pre_calc(int n,int n1){
for (int i=1;i<=n;++i){
cnt1[i]=cnt1[i-1];
cnt2[i]=cnt2[i-1];
if (sa[i]<n1) ++cnt1[i];
else if (sa[i]>n1) ++cnt2[i];
}
}
void get_ans(int l,int r){
if (l>=r) return;
Pr tmp=Lcp(l,r);
int mid=tmp.second,lcp=tmp.first;
ans+=1LL*lcp*(1LL*(cnt1[mid-1]-cnt1[l-1])*(cnt2[r]-cnt2[mid-1])+1LL*(cnt2[mid-1]-cnt2[l-1])*(cnt1[r]-cnt1[mid-1]));
get_ans(l,mid-1);
get_ans(mid,r);
}
}/*}}}*/
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%s\n%s",&s1,&s2);
n1=strlen(s1);
n2=strlen(s2);
n=0;
for (int i=0;i<n1;++i) s[++n]=s1[i];
s[++n]='z'+1;
for (int i=0;i<n2;++i) s[++n]=s2[i];
Sa::get_sa(n);
Sa::get_height(n);
Sa::pre_calc(n,n1+1);
ans=0;
Sa::get_ans(1,n);
printf("%lld\n",ans);
}
【loj2064】找相同字符的更多相关文章
- BZOJ 4566: [Haoi2016]找相同字符 [后缀自动机]
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 275 Solved: 155[Submit][Statu ...
- 【BZOJ4566】找相同字符(后缀数组)
[BZOJ4566]找相同字符(后缀数组) 题面 BZOJ 题解 后缀数组的做法,应该不是很难想 首先看到两个不同的串,当然是接在一起求\(SA,height\) 那么,考虑一下暴力 在两个串各枚举一 ...
- 【BZOJ4566】找相同字符(后缀自动机)
[BZOJ4566]找相同字符(后缀自动机) 题面 BZOJ 题解 看到多串处理,\(SA\)就连起来 \(SAM???\) 单串建自动机 然后其他串匹配 对于一个串建完\(SAM\)后 另一个串在\ ...
- bzoj4566 / P3181 [HAOI2016]找相同字符
P3181 [HAOI2016]找相同字符 后缀自动机 (正解应是广义后缀自动机) 并不会广义后缀自动机. 然鹅可以用普通的后缀自动机. 我们先引入一个问题:算出从一个串内取任意两个不重合子串完全 ...
- 【BZOJ4566】[HAOI2016]找相同字符
[BZOJ4566][HAOI2016]找相同字符 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 其中\(1\le ...
- [BZOJ4566][Haoi2016]找相同字符 后缀自动机+dp
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1212 Solved: 694[Submit][Stat ...
- 【BZOJ4566】[Haoi2016]找相同字符 后缀数组+单调栈
[BZOJ4566][Haoi2016]找相同字符 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同 ...
- bzoj 4566 [Haoi2016]找相同字符SA
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 128 Solved: 75[Submit][Status ...
- [Bzoj4566][Haoi2016]找相同字符(广义后缀自动机)
4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 861 Solved: 495[Submit][Statu ...
- BZOJ_4566_[Haoi2016]找相同字符_后缀自动机
BZOJ_4566_[Haoi2016]找相同字符_后缀自动机 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有 ...
随机推荐
- 【python 3.6】使用itertools.product进行排列组合
#python 3.6 #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'BH8ANK' import itertools colo ...
- 3.Airflow使用
1. airflow简介2. 相关概念2.1 服务进程2.1.1. web server2.1.2. scheduler2.1.3. worker2.1.4. celery flower2.2 相关概 ...
- JS 数组方法 array数组声明 元素的添加和删除 等
声明数组 var arr1 = [1,2,3,4,5]; var arr2 = new Array(100); //声明长度为100的arr2数组. arr2=[]; arr2.length = 10 ...
- 由A到D中间可不止“B、C”
在电子信息系统的学习中,我们或许早就被告知现实世界是模拟的,而数字化的模拟世界则越来越展现更多的风采.但是所谓的数字和模拟只是相对的而已,你可以把模拟量当做无穷数字量的组合,也可以把数字量当做具有不同 ...
- alpha冲6
队名:日不落战队 安琪(队长) 今天完成的任务 回收站前端界面. 明天的计划 查看个人信息界面. 还剩下的任务 信息修改前端界面. 设置界面. 遇到的困难 模拟机莫名其妙就崩了,调试了很久,后在队友的 ...
- 如何通过JAVA让DB2调用操作系统命令
引言:我们在工作中常用操作系统命令和DB2命令对数据库做数据的导入.导出等操作,但是DB2不支持复合SQL 语句调用操作系统命令,因此我们需要利用 UDF 来执行SQL 中不可用的操作(例如:执行一些 ...
- BIND的安装配置
简介 bind是dns协议的一种实现,也就是说,bind仅仅是实现DNS协议的一种应用程序 bind运行后的进程名叫named,不叫bind bind bind的配置文件在:/etc/named.co ...
- Java基于Tomcat Https keytool 自签证书
本文大部分内容系转载,原文地址:https://www.cnblogs.com/littleatp/p/5922362.html keytool 简介 keytool 是java 用于管理密钥和证书的 ...
- 【C】多线程编程笔记
1. pthread_create(pthread类型指针变量 ,NULL ,函数 ,函数参数[多个参数用结构体传]) 2. pthread_join(pthread类型指针变量, 返回一般为null ...
- 小记IptabLes和IptabLex病毒清理过程
去年有台Linux服务器被黑了,看了500万行日志(现在觉得当时好厉害呀),反正当时的日志文件有700Mb以上大.前两天师兄告诉我,信息中心的老师给他说我们有台服务器应该是被人入侵了,当作内网的跳板, ...