BZOJ4566: [Haoi2016]找相同字符

Description

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

Input

两行,两个字符串s1,s2,长度分别为n1,n2。

1 <=n1, n2<= 200000,字符串中只有小写字母

Output

输出一个整数表示答案

Sample Input

aabb
bbaa

Sample Output

10

题解Here!

​$O(n^3)$的大暴力:

首先,我们知道。如果连个串中各取一个极长的相同的子串(极长的意思就是这两个串再向两边延伸就会不同或出界),则这个极长的串的所有子串也是相同的。

由于枚举极长的串并求出其长度并不方便,所以我们可以枚举这个极长的串的左端点,再求出其长度。

这样就可以不重不漏找出所有的极长的串。

更图方便的话,我们可以只考虑两原串中某后缀的所有前缀,这可以补充不漏找出所有的子串。

但是由于我们要在两个字符串中枚举,同时找出子串的长度也是$O(n)$的,于是总复杂度为$O(n^3)$。

$O(n^2)$的优化大暴力:

​刚才说道了后缀的前缀,我们不由自主想到了后缀数组。

如果我们可以很快求出$A$串和$B$串的某两个后缀的最长公共前缀,我们就可以将时间复杂度优化的更低。

这不就是$height$数组解决的嘛!

所以我们可以将两个字符串通过一个分隔符拼接起来,求出$height$数组,即按字典序排序后每个后缀和前一个的$LCP$。

再利用$height$数组和$ST$表,我们就可以$O(1)$得到任意两后缀的$LCP$。

因此,这样做的时间复杂度只有枚举子串起点的复杂度了,即$O(n^2)$。

$O(nlog_2n)$的正解:

​现在拉高时间复杂度的罪魁祸首就是枚举起点了。

所以我们想将其复杂度降低。

考虑到利用$height$数组求任意两个后缀的$LCP$时的独特性质:

两个后缀的$LCP$为字典序排序后他们中间夹的最小的$height$。

也就是说排序后,一个后缀越往后数$LCP$的长度越小。

这样,我们就可以用单调栈维护这个最小值。

分$A$串的子串在前、$B$的子串在前两种情况分别用单调栈求出答案,加起来就行。

至于如何维护,emmm......

​ 由于利用了单调栈这个神奇的手段,时间复杂度降到了$O(nlog_2n)$,也就是倍增处理后缀数组的复杂度。

当然你可以用$DC3$来完成,复杂度可以降到$O(n)$。(当然,本蒟蒻不会啦。。。)

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 400010
using namespace std;
pair<int,long long> stack[MAXN];
int n,l1,l2;
int val[MAXN],sum[MAXN];
char str[MAXN];
int top,sa[MAXN],rk[MAXN],tax[MAXN],tp[MAXN],height[MAXN];
inline int read(){int date=0,w=1;char c=0;while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}return date*w;
}
void radixsort(){for(int i=0;i<=top;i++)tax[i]=0;for(int i=1;i<=n;i++)tax[rk[i]]++;for(int i=1;i<=top;i++)tax[i]+=tax[i-1];for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffixsort(){top=30;for(int i=1;i<=n;i++){rk[i]=val[i];tp[i]=i;}radixsort();for(int w=1,p=0;p<n;top=p,w<<=1){p=0;for(int i=1;i<=w;i++)tp[++p]=n-w+i;for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;radixsort();swap(tp,rk);rk[sa[1]]=p=1;for(int i=2;i<=n;i++)rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;}
}
void getheight(){for(int i=1,j,k=0;i<=n;i++){if(k)k--;j=sa[rk[i]-1];while(val[i+k]==val[j+k])k++;height[rk[i]]=k;}
}
void work(){long long ans=0;top=0;stack[0]=make_pair(1,0);for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]<=l1);for(int i=1;i<=n;i++){while(top&&height[i]<height[stack[top].first])top--;top++;stack[top]=make_pair(i,(long long)(sum[i-1]-sum[stack[top-1].first-1])*height[i]+stack[top-1].second);if(sa[i]>l1+1)ans+=stack[top].second;}top=0;for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]>l1+1);for(int i=1;i<=n;i++){while(top&&height[i]<height[stack[top].first])top--;top++;stack[top]=make_pair(i,(long long)(sum[i-1]-sum[stack[top-1].first-1])*height[i]+stack[top-1].second);if(sa[i]<=l1+1)ans+=stack[top].second;}printf("%lld\n",ans);
}
void init(){scanf("%s",str+1);l1=n=strlen(str+1);str[++n]='z'+1;scanf("%s",str+n+1);n=strlen(str+1);for(int i=1;i<=n;i++)val[i]=str[i]-'a'+1;suffixsort();getheight();
}
int main(){init();work();return 0;
}


以上是后缀数组解法,当然你可以用$SAM$吊打$SA$,就像$AC$自动机吊打$Trie$和$KMP$一样。。。

(坑,未填。。。)

转载于:https://www.cnblogs.com/Yangrui-Blog/p/9451807.html

BZOJ4566: [Haoi2016]找相同字符相关推荐

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

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

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

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

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

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

  4. [BZOJ4566][HAOI2016]找相同字符 后缀自动机

    题目要求的就是B的每个字串在A中的出现次数之和. 我们考虑先建出A串的SAM,每个点所代表的串的个数就是 |Righti|∗(Maxi−Maxfai) |Right_i|*(Max_i-Max_{fa ...

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

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

  6. 【HAOI2016/BZOJ4566】找相同字符 后缀数组+单调栈

    原题走这里 鉴于我实在不是很懂单调栈和单调队列这一系列东西,所以我决定稍微具体讲一下单调栈. 恩,本题实质上就是求两个字符串的公共子串数,其中只要出现位置不同,就算是不同的子串. 处理多个字符串的经典 ...

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

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

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

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

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

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

最新文章

  1. (C++)string 的两种输入方式和输出方式
  2. 业界首个面向NLP场景深度迁移学习框架
  3. mysql主从复制监控shell脚本
  4. tableau实战系列(十八)-通过可视化实现购物篮关联分析( Market Basket Analysis),关联物品之间的关联关系
  5. Eliminate Witches!【2011年北京赛区正赛赛题-2】
  6. android 中文 api (72) —— BluetoothSocket[蓝牙]
  7. 总结2010展望2011
  8. mysql 数据库授权(给某个用户授权某个数据库)
  9. [UI] 精美UI界面欣赏[8]
  10. ASP.NET MVC学习---(一)ORM框架,EF实体数据模型简介
  11. 【转载】Delphi下实现鼠标自动点击器
  12. 一个简单的python登录验证系统
  13. PostgreSQL使用pgAdmin3不能编辑表里的数据
  14. JS手册和参考教程网址
  15. 7. Java8新特性-并行数据处理(parallel)
  16. 总有云开日出时候, 万丈阳光照耀你我
  17. python3常用模块_Python学习笔记三(常用模块)
  18. 纯CSS 实现知乎滑动广告效果
  19. 7.微信小程序做按比例截取图片
  20. 定时任务实现的几种方式

热门文章

  1. 合并两个有序数组(重新开始)
  2. java nio设计模式_Java NIO:浅析I/O模型
  3. html按钮线性炫光,6分钟实现CSS炫光倒影按钮 html+css
  4. extra加ing_英语词汇学各个章节的内容
  5. android 网络编程实现,Android开发使用HttpURLConnection进行网络编程详解【附源码下载】...
  6. python中adb连接手机_Python脚本利用adb进行手机控制的方法
  7. 不停刷朋友圈的人_刷爆朋友圈的推拉门安装方式 90%的人从没见过
  8. Linux文件中的stat结构
  9. 使用Java对轨迹进行抽稀,并生成mvt(Map Vector Tile)瓦片
  10. Java IDEA import sun.reflect.ConstructorAccessor报错