zoukankan      html  css  js  c++  java
  • 【POJ 3415】Common Substrings

    【链接】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
    ", ans);
    	}
    	return 0;
    }


  • 相关阅读:
    iOS项目中常见的文件
    RN 导入原有Xcode项目中,引入Pod依赖出现的问题与解决
    Xcode中StoryBoard Reference 新特性的使用
    非等高cell实战(01)-- 实现微博页面
    iOS回顾笔记(09) -- Cell的添加、删除、更新、批量操作
    iOS回顾笔记(08) -- 自定义Cell的类型和创建步骤总结
    unittest中的测试固件
    unittest测试用例的执行顺序
    unittest各个组件之间的关系
    ubuntu16.04开机时的.local问题
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7625994.html
Copyright © 2011-2022 走看看