题目描述

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

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

题解:

s1'#'s2拼起来。

求height

要求∑(i)∑(j)LCP(rk[i],rk[j]) 1<=i<=l1, l1+2<=j<=l1+l2+1

考虑找排名序列。

其实是对属于s1的i之前的,属于s2的j,min(hei[k]),j+1<=k<=i

对一些前面的某些位置取min再做和

可以分治。

对于跨中点的区间,钦定最小值在左边,做一遍

钦定最小值在右边,做一遍

对于两边最小值相同的,一个取等一个不取等

height[i]=lcp(suff[sa[i],suff[sa[i-1]])

注意边界处理

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=200000+5;
const int inf=0x3f3f3f3f;
int n,m;
int l1,l2;
char s1[N],s2[N],s[2*N];
int x[4*N],y[4*N],rk[2*N],sa[2*N];
int c[2333],num;
int hei[2*N];
void getsa(){m=233;for(reg i=1;i<=n;++i) ++c[x[i]=s[i]];//for(reg i=1;i<=n;++i) cout<<x[i]<<" ";cout<<endl;for(reg i=2;i<=m;++i) c[i]+=c[i-1];for(reg i=1;i<=n;++i) sa[c[x[i]]--]=i;//for(reg i=1;i<=n;++i) cout<<sa[i]<<" ";cout<<endl;for(reg k=1;k<=n;k<<=1){num=0;for(reg i=n-k+1;i<=n;++i) y[++num]=i;for(reg i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k;for(reg i=1;i<=m;++i) c[i]=0;for(reg i=1;i<=n;++i) ++c[x[i]];for(reg i=2;i<=m;++i) c[i]+=c[i-1];for(reg i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;swap(x,y);num=1;x[sa[1]]=1;for(reg i=2;i<=n;++i){x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;}if(num==n) break;m=num;}
}
void gethei(){int k=0;for(reg i=1;i<=n;++i) rk[sa[i]]=i;for(reg i=1;i<=n;++i){if(rk[i]==1) continue;if(k) --k;int j=sa[rk[i]-1];while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;hei[rk[i]]=k;}
}
ll ans;
int be[2*N];
void divi(int l,int r){
//    cout<<" divi "<<l<<" and "<<r<<"-------------------------------"<<endl;if(l==r) return ;int mid=(l+r)>>1;
//    cout<<" mid "<<mid<<endl;int cnt1=0,cnt2=0;int mir=inf,mil=inf;int ptr=mid+1;for(reg i=mid+1;i<=r;++i){mir=min(mir,hei[i]);while(ptr-1>=l&&hei[ptr]>=mir) {if(be[ptr-1]==1) ++cnt1;else if(be[ptr-1]==2) ++cnt2;ptr--;}if(be[i]==1) ans+=(ll)cnt2*mir;else if(be[i]==2) ans+=(ll)cnt1*mir;}cnt1=0,cnt2=0;ptr=mid+1;for(reg i=mid;i>=l;--i){mil=min(mil,hei[i+1]);while(ptr<=r&&hei[ptr]>mil){if(be[ptr]==1) ++cnt1;else if(be[ptr]==2) ++cnt2;ptr++;}if(be[i]==1) ans+=(ll)cnt2*mil;else if(be[i]==2) ans+=(ll)cnt1*mil;}divi(l,mid);divi(mid+1,r);
}
int main(){scanf("%s%s",s1+1,s2+1);l1=strlen(s1+1);l2=strlen(s2+1);n=l1+l2+1;for(reg i=1;i<=l1;++i) s[i]=s1[i];s[l1+1]='#';for(reg i=1;i<=l2;++i) s[l1+1+i]=s2[i];//    cout<<n<<" : "<<s+1<<endl;
    getsa();gethei();for(reg i=1;i<=l1;++i) be[rk[i]]=1;be[rk[l1+1]]=3;for(reg i=1;i<=l2;++i) be[rk[l1+1+i]]=2;//    for(reg i=1;i<=n;++i){
//        cout<<hei[i]<<" ";
//    }cout<<endl;
//    for(reg i=1;i<=n;++i){
//        cout<<be[i]<<" ";
//    }cout<<endl;
//
    divi(1,n);printf("%lld",ans);return 0;
}}
int main(){Miracle::main();return 0;
}/*Author: *Miracle*Date: 2018/11/15 10:51:22
*/

更优秀地,可以用单调栈做;

从顶到底,高度单调递减,高度即位置与当前i之间的height取min

维护这个矩形集合的面积。

遇到hei[i]<hei[top]的情况,直接砍下去,然后必要的时候,和sta[top-1]合并

stack中每个元素维护w和h,表示矩形宽和高。

全局数组now维护面积

s1,s2在前分别做一遍

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=200000+5;
const int inf=0x3f3f3f3f;
int n,m;
int l1,l2;
char s1[N],s2[N],s[2*N];
int x[4*N],y[4*N],rk[2*N],sa[2*N];
int c[2333],num;
int hei[2*N];
struct po{int w,h;po(){};po(int a,int b){w=a,h=b;}
}sta[2*N];
int top;
void getsa(){m=233;for(reg i=1;i<=n;++i) ++c[x[i]=s[i]];//for(reg i=1;i<=n;++i) cout<<x[i]<<" ";cout<<endl;for(reg i=2;i<=m;++i) c[i]+=c[i-1];for(reg i=1;i<=n;++i) sa[c[x[i]]--]=i;//for(reg i=1;i<=n;++i) cout<<sa[i]<<" ";cout<<endl;for(reg k=1;k<=n;k<<=1){num=0;for(reg i=n-k+1;i<=n;++i) y[++num]=i;for(reg i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k;for(reg i=1;i<=m;++i) c[i]=0;for(reg i=1;i<=n;++i) ++c[x[i]];for(reg i=2;i<=m;++i) c[i]+=c[i-1];for(reg i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;swap(x,y);num=1;x[sa[1]]=1;for(reg i=2;i<=n;++i){x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;}if(num==n) break;m=num;}
}
void gethei(){int k=0;for(reg i=1;i<=n;++i) rk[sa[i]]=i;for(reg i=1;i<=n;++i){if(rk[i]==1) continue;if(k) --k;int j=sa[rk[i]-1];while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;hei[rk[i]]=k;}
}
ll ans;
ll now;
int be[2*N];
int sum1[2*N],sum2[2*N];
int main(){scanf("%s%s",s1+1,s2+1);l1=strlen(s1+1);l2=strlen(s2+1);n=l1+l2+1;for(reg i=1;i<=l1;++i) s[i]=s1[i];s[l1+1]='#';for(reg i=1;i<=l2;++i) s[l1+1+i]=s2[i];//    cout<<n<<" : "<<s+1<<endl;
    getsa();gethei();for(reg i=1;i<=l1;++i) be[rk[i]]=1;be[rk[l1+1]]=3;for(reg i=1;i<=l2;++i) be[rk[l1+1+i]]=2;for(reg i=2;i<=n;++i){//cout<<" iiiiii "<<i<<" ------------------------ "<<endl;while(top&&hei[i]<sta[top].h){if(top>1){if(sta[top-1].h>hei[i]){now-=(sta[top].h-sta[top-1].h)*sta[top].w;sta[top-1].w+=sta[top].w;--top;}else{now-=(sta[top].h-hei[i])*sta[top].w;sta[top].h=hei[i];}}else{now-=(sta[top].h-hei[i])*sta[top].w;sta[top].h=hei[i];}}sta[++top]=po(be[i-1]==1,hei[i]);now+=(be[i-1]==1)*hei[i];if(be[i]==2) ans+=now;//cout<<" ans "<<ans<<" now "<<now<<endl;
    }top=0;now=0;for(reg i=2;i<=n;++i){while(top&&hei[i]<sta[top].h){if(top>1){if(sta[top-1].h>hei[i]){now-=(sta[top].h-sta[top-1].h)*sta[top].w;sta[top-1].w+=sta[top].w;--top;}else{now-=(sta[top].h-hei[i])*sta[top].w;sta[top].h=hei[i];}}else{now-=(sta[top].h-hei[i])*sta[top].w;sta[top].h=hei[i];}}sta[++top]=po(be[i-1]==2,hei[i]);now+=(be[i-1]==2)*hei[i];if(be[i]==1) ans+=now;}printf("%lld",ans);return 0;
}}
int main(){Miracle::main();return 0;
}/*Author: *Miracle*Date: 2018/11/15 10:51:22
*/

转载于:https://www.cnblogs.com/Miracevin/p/9963765.html

[HAOI2016]找相同字符相关推荐

  1. BZOJ4566: [Haoi2016]找相同字符

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

  2. [洛谷P3181] [HAOI2016]找相同字符

    洛谷题目链接:[HAOI2016]找相同字符 题目描述 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式 输入 ...

  3. [bzoj4566][HAOI2016]找相同字符

    4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MB Submit: 113 Solved: 64 [Submit][Status ...

  4. 4566: [Haoi2016]找相同字符 SAM

    折腾了好久.不过收获还是很多的.第一次自己去画SAM所建出来fail树.深入体会了这棵树的神奇性质. 当然,我最终靠着自己A掉了.(这是我第一次推SAM的性质(以前都是抄别人的,感觉自己好可耻),不过 ...

  5. BZOJ 4566 [Haoi2016]找相同字符

    后缀数组 或 后缀自动机 后缀数组的做法 O(nlogn) O(nlogn) : 串接起来.要求的就是后缀数组中不属于同一串的后缀的LCP.暴力做 O(n2) O(n^2) .发现我们一直都在区间取m ...

  6. [bzoj4566][HAOI2016]找相同字符(后缀数组)

    题目 传送门 题解 这里:把两个串用一个很大的字符连接起来,求一个后缀数组. 考虑怎样暴力的算答案. 在 rank  r a n k rank数组中从前往后枚举起点,对于每个枚举的起点,都暴力的往后扫 ...

  7. Bzoj4566:[HAOI2016]找相同字符

    题面 Bzoj Sol 两个串拼在一起后求出后缀数组 然后显然的\(n^2\)暴力,就是直接枚举求\(LCP\) 又由于扫的时候是对\(height\)取\(min\) 那么可以用单调栈维护每一段的贡 ...

  8. 【bzoj4566】[Haoi2016]找相同字符【后缀自动机】

    题目传送门 题解:在文本串上建后缀自动机,用模式串在后缀自动机上跑.扫一遍模式串,在后缀自动机上走,走不了就跳fail再走. 走的过程中,维护模式串与文本串匹配的最大长度,并且统计答案. 怎么统计答案 ...

  9. BZOJ4566: [Haoi2016]找相同字符(后缀自动机)

    题意 题目链接 Sol 直接在SAM上乱搞 枚举前缀,用SAM统计可以匹配的后缀,具体在匹配的时候维护和当前节点能匹配的最大值 然后再把parent树上的点的贡献也统计上,这部分可以爆跳parent树 ...

最新文章

  1. 基于yolov5的工业缺陷检测方案
  2. 经管资源库项目的总结笔记
  3. 第二章:搭建Android开发环境(读后感)
  4. 分区报无效的参数_西门子70系列变频器55KW上电就报F002故障维修
  5. 台式计算机 启天 m4600,联想启天M4600-N000:高效的焕新之选——台式机
  6. 统计学习导论_统计机器学习之扫盲导论篇
  7. 一道大题决定去留:为什么synchronized无法禁止指令重排,却能保证有序性?
  8. Bootstrap-组件-2
  9. 《Go 语言程序设计》读书笔记 (五) 协程与通道
  10. 设置,获取和删除Cookies
  11. 在Java中使用Rserve调用R
  12. 两个卡巴斯基 6.0 官方简体中文版授权文件
  13. 数据库课程设计-题库管理系统
  14. Python 自动识别图片文字—保姆级OCR实战教程
  15. 聚类算法Kmens和密度峰值聚类
  16. Java各种学习资源(视频+文档)
  17. String,无所不在的数据类型
  18. django haystack一次使用总结
  19. apk protect下载地址
  20. 介绍一个全局最优化的方法:随机游走算法(Random Walk)

热门文章

  1. 多任务的同步与相互排斥
  2. 《技术管理之巅》读书笔记
  3. 红黑树-想说爱你不容易
  4. 大数据_MapperReduce_协处理器_类似Mysql的触发器---Hbase工作笔记0024
  5. SpringCloud工作笔记087---SpringBoot启动报错:IDEA 错误: 找不到或无法加载主类 解决方法_connected to the target VM, address:
  6. img2txt(二)
  7. tensflow andaconda 方式的安装
  8. python越来越慢_为什么我的算法越来越慢?
  9. linux下tomcat发布网站验证码获取不到
  10. java中class文件如何加载的_jvm如何加载class文件