【链接】h在这里写链接


【题意】


    求两个串的长度大于等于k的公共子串个数。

    相同的重复计数。

【题解】


    先把两个字符串用一个分隔符分开。最好比出现的字符都大的一个数字。

    然后,对于这一个字符串,求出它的Height和Sa数组。

    然后,把height 数组【连续】大于等于k的,分成一组去考虑。

    (没有大于等于k了的话,显然子串的长度就不符合要求)



    如果我们在【这一组】里遇到了一个a串的后缀。

    那么显然,我们会在这个组里再找若干个b串的后缀。

    然后分别求出这个a串和这些b串的最长公共前缀temp,然后累加temp-k+1就好.



    但是这样实现的话,时间复杂度为O(n^2)



    有一个性质,就是任意两个后缀的最长公共前缀为它们之间(排名之间)的height的最小值。



    根据这个,用单调队列来做一个优化。

    对于连续的大于等于k的height值。

    i顺序扫过去。

        遇到的如果是a串,那么直接cnt累加height[i]-k+1,然后把它放到单调队列里面

        去,之后,如果再遇到a串的话,就看看是不是这个a串的height比之前的a串的height

        值更小,如果更小的话,就用新的,更小的height去取代旧的且比较大的height;

        (因为遇到了更小的,就说明,后面的b串如果要和a串递增答案,用的temp值就

        更小了,则cnt也会相应改变)

        这个过程就对应了,把单调队列的队尾的大的height给去掉。

        然后更新cnt;(cnt是下次遇到b串的时候累加答案的)

        然后把这个height[i]直接加到队列尾巴.

        因为遇到了一个新的a的后缀,所以后面的b如果要和这个a后缀做答案,

        肯定要从这个height[i]开始算最小值,后面如果有最小值,则后面再用

        上面的方法改就好。

        (之前虽然有更小的height,但是他们在[l,r]这个区间的左边。

        因此把它加到队列是没有问题的.



        如果遇到的是b串的后缀,那么,也应该用这个b串的后缀,来尝试改变一下单调

        队列里面的最小值,如果可改的话,那么他就取代了某些a串的height值了。还

        是一样,弹出队列尾巴比它大的。把这个新的加进去(因为是b的后缀,所以这个

        加入到队列尾巴以后,除非他取代了之前的a串的height,否则不计算个数),然

        后更改cnt的值。因为是b串,所以把之前的a串的cnt值累加进最后的答案就好。

        (所以这个cnt值,就相当于是预先算出来了前面的所有a的后缀,和下一个会遇

        到的b的后缀的答案的和)



    然后要反过来做一遍。

    即把b放在前面,遇到a累加b的答案。

    比如

    AABBAA

    (A和B代表了A和B的一个后缀)

    如果只做第一种

    3->5

    4->5

    3->6

    4->6

    这4种就会漏掉

【错的次数】


0

【反思】


转化模型:

给你n个数字ai,

每个数字属于A或属于B;

对于所有属于B的数字a[i]

求出j<i;

且a[j]属于A,

temp = ∑(min(a[j..i])-k+1)

对于所有的a[i]的temp值求和



【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std; int k; const int N = 2e5;
const int MAX_CHAR = 255;//每个数字的最大值。
char s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存
int Sa[N + 10], T1[N + 10], T2[N + 10], C[N + 10];
int Height[N + 10], Rank[N + 10];
int cnt[N + 10], dl[N + 10], tail; void build_Sa(int n, int m) {
int i, *x = T1, *y = T2;
for (i = 0; i<m; i++) C[i] = 0;
for (i = 0; i<n; i++) C[x[i] = s[i]]++;
for (i = 1; i<m; i++) C[i] += C[i - 1];
for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
for (i = n - k; i<n; i++) y[p++] = i;
for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k;
for (i = 0; i<m; i++) C[i] = 0;
for (i = 0; i<n; i++) C[x[y[i]]]++;
for (i = 1; i<m; i++) C[i] += C[i - 1];
for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[Sa[0]] = 0;
for (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;
}
} void getHeight(int n)
{
int i, j, k = 0;
for (i = 1; i <= n; i++) Rank[Sa[i]] = i;
for (i = 0; i<n; i++) {
if (k) k--;
j = Sa[Rank[i] - 1];
while (s[i + k] == s[j + k]) k++;
Height[Rank[i]] = k;
}
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
while (~scanf("%d", &k) && k)
{
scanf("%s", s);
int l1 = strlen(s);
s[l1] = 'z' + 1;//分隔符
scanf("%s", s + l1 + 1);
int n = strlen(s);
s[n] = 0;//一个字符就链接完成了 //然后开始求后缀数组
build_Sa(n + 1, MAX_CHAR);//调用n+1
getHeight(n); //后缀数组求完了
//开始做算法 tail = 0;
long long temp = 0,ans =0;
for (int i = 2; i <= n-1; i++)//求连续的大于等于k的组,a->b
if (Height[i]>=k){
int num = 0;
while (tail >= 1 && Height[i] < dl[tail])
{//比Height[i]小的都去掉,因为区间最值肯定变小了
num+=cnt[tail];//加上a的后缀个数
temp -= 1LL*cnt[tail] * (dl[tail] - k + 1);
temp += 1LL*cnt[tail] * (Height[i] - k + 1);//改变累加值
tail--;
}
tail++;//把height[i]放到队列尾巴
dl[tail] = Height[i];
if (Sa[i-1] < l1)//是一个a串
{
temp += (Height[i] - k + 1);//累加它对后面的b的影响
cnt[tail] = num + 1;//把它看成一个整体了,前面的被他
//"吃掉的"都和它一样了。
}
else
{
//是一个b串
cnt[tail] = num;//他本身没有影响
}
if (Sa[i] > l1)//height[i]的影响范围只限于...i
//height[i+1]的影响范围是..i+1
//所以如果i是b串的话,它的height值已经算进去了
//可以想一下i=2时的运行情况。
{
ans += temp;
//i和j的lcp
//为height[i+1]..height[j]里面的最小值
}
}
else
{
tail = 0, temp = 0;
} temp = 0, tail = 0;
for (int i = 2; i <= n; i++)//求连续的大于等于k的组,b->a
if (Height[i] >= k) {
int num = 0;
while (tail >= 1 && Height[i] < dl[tail])
{//比Height[i]小的都去掉,因为区间最值肯定变小了
num += cnt[tail];//加上b的后缀个数
temp -= 1LL*cnt[tail] * (dl[tail] - k + 1);
temp += 1LL*cnt[tail] * (Height[i] - k + 1);//改变累加值
tail--;
}
tail++;//把height[i]放到队列尾巴
dl[tail] = Height[i];
if (Sa[i - 1] > l1)//是一个b串
{
temp += (Height[i] - k + 1);//累加它对后面的a的影响
cnt[tail] = num + 1;//把它看成一个整体了,前面的被他
//"吃掉的"都和它一样了。
}
else
{
//是一个a串
cnt[tail] = num;//他本身没有影响
}
if (Sa[i] < l1)//height[i]的影响范围只限于...i
//height[i+1]的影响范围是..i+1
//所以如果i是a串的话,它的height值已经算进去了
//可以想一下i=2时的运行情况。
{
ans += temp;
//i和j的lcp
//为height[i+1]..height[j]里面的最小值
}
}
else
{
tail = 0, temp = 0;
}
printf("%lld\n", ans);
}
return 0;
}

【POJ 3415】Common Substrings的更多相关文章

  1. 【POJ 3415】Common Substrings 长度不小于k的公共子串的个数

    长度不小于k的公共子串的个数,论文里有题解,卡了一上午,因为sum没开long long!!! 没开long long毁一生again--- 以后应该早看POJ里的Discuss啊QAQ #inclu ...

  2. 【POJ - 1458】Common Subsequence(动态规划)

    Common Subsequence Descriptions: A subsequence of a given sequence is the given sequence with some e ...

  3. 【POJ 1201】 Intervals(差分约束系统)

    [POJ 1201] Intervals(差分约束系统) 11 1716的升级版 把原本固定的边权改为不固定. Intervals Time Limit: 2000MS   Memory Limit: ...

  4. bzoj 2295: 【POJ Challenge】我爱你啊

    2295: [POJ Challenge]我爱你啊 Time Limit: 1 Sec  Memory Limit: 128 MB Description ftiasch是个十分受女生欢迎的同学,所以 ...

  5. 【链表】BZOJ 2288: 【POJ Challenge】生日礼物

    2288: [POJ Challenge]生日礼物 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 382  Solved: 111[Submit][S ...

  6. BZOJ2288: 【POJ Challenge】生日礼物

    2288: [POJ Challenge]生日礼物 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 284  Solved: 82[Submit][St ...

  7. BZOJ2293: 【POJ Challenge】吉他英雄

    2293: [POJ Challenge]吉他英雄 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 80  Solved: 59[Submit][Stat ...

  8. BZOJ2287: 【POJ Challenge】消失之物

    2287: [POJ Challenge]消失之物 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 254  Solved: 140[Submit][S ...

  9. BZOJ2295: 【POJ Challenge】我爱你啊

    2295: [POJ Challenge]我爱你啊 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 126  Solved: 90[Submit][Sta ...

随机推荐

  1. Js将类数组转化为数组

    说起伪数组,大家可能会想到arguments, 这个我们函数参数的一个类数组,是类数组的代表. 1.拥有length属性,可以使用下标来访问元素,这两点和数组相同. 2.不能使用数组的方法,他们不能使 ...

  2. shutdown---系统关机

    shutdown命令用来系统关机命令.shutdown指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作. 语法 shutdown(选项)(参数) 选项 -c:当执行“shutdown - ...

  3. 2048游戏分析、讨论与扩展 - Part I - 游戏分析与讨论

    2048这个游戏从刚出開始就风靡整个世界. 本技术博客的目的是想对2048涉及到相关的全部问题进行仔细的分析与讨论,得到一些大家能够接受而且理解的结果. 在这基础上,扩展2048的游戏性,使其变得更好 ...

  4. linux 批量设置文件夹755 文件644权限

    linux 批量设置文件夹755 文件644权限 文件来源 http://www.111cn.net/sys/linux/109724.htm 本文章来为各位介绍一篇关于linux 批量设置文件夹75 ...

  5. code blocks常用快捷键

    CodeBlocks常用操作快捷键 编辑部分: Ctrl + A:全选 Ctrl + C:复制 Ctrl + X: 剪切 Ctrl + V:粘贴 Ctrl + Z:撤销(后退一步) Ctrl + S: ...

  6. the way to go

    推荐两本书 关于go语言的 The way to go Go语言编程高清完整版电子书.pdf

  7. 你真的懂Flask中浅谈蓝图Blueprint吗?

    一,什么是Flask中的蓝图Blueprint Blueprint是用于实现Flask框架中单个应用的视图,模板,静态文件的集合. Blueprint 是一个存储操作(路由映射)方法的容器,这些操作在 ...

  8. ListCtrl添加右键菜单(在对话框类中)

    在对话框类中如何添加NM_RCLICK消息: ListCtrl控件右键单击选择属性 在右侧属性栏中选择控件事件 在控件事件中找到NM_RCLICK并添加 完成,写代码

  9. 《ASP.NET》数据绑定—DropDownList、ListBox

    DropDownList和ListBox实现两级联动功能.他们也能够将从后台数据库中搜选的出来的信息加以绑定.这里要实现的功能是在DropDownList中选择"省",然后让Lis ...

  10. 高可用架构篇--MyCat在MySQL主从复制基础上实现读写分离

    实战操作可参考:http://www.roncoo.com/course/view/3117ffd4c74b4a51a998f9276740dcfb 一.环境 操作系统:CentOS-6.6-x86_ ...