poj3415_Common Substrings
题意
给定两个字符串,求长度大于等于k的公共子串数。
分析
- 将两个字符串中间加个特殊字符拼接,跑后缀数组。
- 将题目转化为对每一个后缀求\(\sum_{j=1}^{i-1}lcp(i,j)\),且后缀\(i\)和\(j\)属于不同字符串。
- 由于\(lcp\)只跟\(h\)数组的区间最小值有关,因此对于单调递减的\(h[i]\)我们可以合并贡献和个数,维护一个单调栈。
- 分别统计\(a\)串对\(b\)的贡献和\(b\)串对\(a\)的贡献。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=3e5+50;
char a[N],b[N],s[N];
int sa[N],rk[N],h[N];
int t[N],t2[N],c[N];
int al,bl,n,k;
void build(int n,int m){
n++;
int *x=t,*y=t2;
for(int i=0;i<m;i++){
c[i]=0;
}
for(int i=0;i<n;i++){
c[x[i]=s[i]]++;
}
for(int i=1;i<m;i++){
c[i]+=c[i-1];
}
for(int i=n-1;i>=0;i--){
sa[--c[x[i]]]=i;
}
for(int k=1;k<=n;k*=2){
int p=0;
for(int i=n-k;i<n;i++){
y[p++]=i;
}
for(int i=0;i<n;i++){
if(sa[i]>=k){
y[p++]=sa[i]-k;
}
}
for(int i=0;i<m;i++){
c[i]=0;
}
for(int i=0;i<n;i++){
c[x[y[i]]]++;
}
for(int i=1;i<m;i++){
c[i]+=c[i-1];
}
for(int i=n-1;i>=0;i--){
sa[--c[x[y[i]]]]=y[i];
}
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<n;i++){
x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
}
if(p>=n){
break;
}
m=p;
}
n--;
for(int i=0;i<=n;i++){
rk[sa[i]]=i;
}
int k=0;
for(int i=0;i<n;i++){
if(k){
k--;
}
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]){
k++;
}
h[rk[i]]=k;
}
}
void debug(){
for(int i=1;i<=n;i++){
for(int j=sa[i];j<n;j++){
printf("%c",s[j]);
}
printf("\n");
}
for(int i=1;i<=n;i++){
printf("%d ",sa[i]);
}
printf("\n");
for(int i=0;i<n;i++){
printf("%d ",rk[i]);
}
printf("\n");
for(int i=1;i<=n;i++){
printf("%d ",h[i]);
}
printf("\n");
}
//答案就是对任意两个不同后缀a[i...]和b[j...]的sum(lcp(ai,bj)-k+1)
//两个单调栈,一个维护h[i],一个维护贡献之和
ll he[N],ct[N];
ll solve(){
//考虑用单调栈优化到O(n),即对于每一个后缀求与前面后缀的lcp之和,不重不漏
ll ans=0;
//当前后缀与前面每个后缀的lcp之和
//由性质可知,当前后缀和前面某一个后缀的lcp应该是之间的h[i]最小值
//因此可以将递减的h[i]合并为最小的那个h[min]*cnt
ll sum=0;
int tp=0;
for(int i=2;i<=n;i++){
if(h[i]<k){
tp=0;
sum=0;
continue;
}
ll cnt=0;
//维护单调栈,由于lcp只跟区间h最小值有关,将所有栈顶大于当前h[i]的都合并
while(tp && he[tp]>h[i]){
//减去无效栈顶的贡献(h[i]-k+1)
sum-=(he[tp]-k+1)*ct[tp];
//暂时累计cnt,存储到新的栈顶
cnt+=ct[tp];
//栈顶出栈
tp--;
}
//入栈,保持单调性
he[++tp]=h[i];
if(sa[i-1]<al) {
//有效贡献的串,个数加1
cnt++;
}
ct[tp]=cnt;
//累加栈顶贡献
sum+=(he[tp]-k+1)*ct[tp];
if(sa[i]>al){
//将当前累加的贡献加到答案中,即b串后缀与前面所有a串后缀的lcp之和
ans+=sum;
}
}
tp=sum=0;
for(int i=2;i<=n;i++){
if(h[i]<k){
tp=0;
sum=0;
continue;
}
ll cnt=0;
while(tp && he[tp]>h[i]){
sum-=(he[tp]-k+1)*ct[tp];
cnt+=ct[tp];
tp--;
}
if(sa[i-1]>al){
he[++tp]=h[i];
ct[tp]=cnt+1;
sum+=(he[tp]-k+1)*ct[tp];
}else{
he[++tp]=h[i];
ct[tp]=cnt;
sum+=(he[tp]-k+1)*ct[tp];
}
//累加b串后缀与前面所有a串后缀的lcp之和
if(sa[i]<al){
ans+=sum;
}
}
return ans;
}
int main(){
// freopen("in.txt","r",stdin);
while(~scanf("%d",&k) && k){
scanf("%s",a);
scanf("%s",b);
al=strlen(a);
bl=strlen(b);
for(int i=0;i<al;i++){
s[i]=a[i];
}
s[al]='~';
for(int i=0;i<bl;i++){
s[al+1+i]=b[i];
}
n=al+bl+1;
s[n]='\0';
build(n,300);
// debug();
ll ans=solve();
printf("%lld\n",ans);
}
return 0;
}
poj3415_Common Substrings的更多相关文章
- [LeetCode] Unique Substrings in Wraparound String 封装字符串中的独特子字符串
Consider the string s to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz" ...
- Leetcode: Unique Substrings in Wraparound String
Consider the string s to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz" ...
- CSU-1632 Repeated Substrings (后缀数组)
Description String analysis often arises in applications from biology and chemistry, such as the stu ...
- CF451D Count Good Substrings (DP)
Codeforces Round #258 (Div. 2) Count Good Substrings D. Count Good Substrings time limit per test 2 ...
- LA4671 K-neighbor substrings(FFT + 字符串Hash)
题目 Source http://acm.hust.edu.cn/vjudge/problem/19225 Description The Hamming distance between two s ...
- 后缀数组---New Distinct Substrings
Description Given a string, we need to find the total number of its distinct substrings. Input T- nu ...
- Codeforces Round #258 D Count Good Substrings --计数
题意:由a和b构成的字符串,如果压缩后变成回文串就是Good字符串.问一个字符串有几个长度为偶数和奇数的Good字串. 分析:可知,因为只有a,b两个字母,所以压缩后肯定为..ababab..这种形式 ...
- SPOJ 694. Distinct Substrings (后缀数组不相同的子串的个数)转
694. Distinct Substrings Problem code: DISUBSTR Given a string, we need to find the total number o ...
- Codeforces Round #306 (Div. 2) A. Two Substrings 水题
A. Two Substrings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/550/pro ...
随机推荐
- +p解决vim粘贴自动缩进。 数字gg跳到vim指定行。 vim查找到后,enter键修改
+p解决vim粘贴自动缩进. 数字gg跳到vim指定行. vim查找到后,enter键修改
- Excel催化剂开源第44波-窗体在Show模式下受Excel操作影响变为最小化解决方式
在Excel催化剂的许多功能中,都会开发窗体用于给用户更友好的交互使用,但有一个问题,困扰许久,在窗体上运行某些代码后,中途弹出下MessageBox对话框给用户做一些简单的提示或交互时,发现程序运行 ...
- Shrio多Realm认证及认证策略
在大型的系统中,安全数据可能会存放在多个数据库中,而且每个数据的加密方式也是不一样的,那么单一的Realm就无法完成. 这时,就需要用到多Realm认证了,多Realm又涉及到认证策略,及在多个Rea ...
- 【小程序】小程序中设置 tabBar
小程序中 tabBar 的设置,tabBar 就是底部导航栏,在app.json中配置. list 为数组至少两项.tab栏的 position 为 top 时间,不显示图标. "tabBa ...
- Linux卸载MySql——ubuntu版
卸载mysql 1)删除mysql的数据文件 sudo rm /var/lib/mysql/ -R 2)删除mqsql的配置文件 sudo rm /etc/mysql/ -R 3)自动卸载mysql的 ...
- linux初学者-虚拟机联网篇
linux初学者-虚拟机联网篇 在虚拟机的使用过程中,本机可以连接WIFI直接上网,但是有时候需要用到虚拟机的联网,那么在本机联网的情况下,虚拟机怎么联网呢?接下来将介绍如何在本机已经连接到WIFI的 ...
- HTTP,TCP,IP详解
互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议. 通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址.互联网上每 ...
- RocketMQ中Producer的启动源码分析
RocketMQ中通过DefaultMQProducer创建Producer DefaultMQProducer定义如下: public class DefaultMQProducer extends ...
- Android开发——通过wifi接收IPCamera视频流
前面,我们已经了解了怎么在android app上打开关闭和扫描,搜索wifi,现在,我来写一下怎么通过连接wifi来使app获取到IPCamera摄像头的视频. 一.通过URL获取视频的地址 二.创 ...
- cesium学习——cesium中的坐标
一.坐标展现形式 在cesium中,对于坐标数值单位有三种:角度.弧度和坐标值 1.角度 角度就是我们所熟悉的经纬度,对于地球的坐标建立如下: 图中以本初子午线作为x和z的面,建立了一个空间坐标系.可 ...