poj 2774 后缀数组 最长公共子串

题意:求两个长度最长是100000的字符串的公共子串。

思路:参考罗穗骞的论文,就是在两个字符串中间加一个字符,利用倍增算法求height数组,height数组存储了排名相邻的两个后缀的最长公共子串长度,所以枚举新形成的字符串的height,同时要保证相邻两个后缀字符串是分别属于这两个字符串的。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 200010;
int num[maxn];
int sa[maxn] , rank[maxn] , height[maxn];
int wa[maxn] , wb[maxn] , wv[maxn] , wd[maxn];
int cmp(int *r , int a , int b , int l) {
    return r[a] == r[b] && r[a+l] == r[b+l];    
}
void da(int *r , int n , int m) {         //r是待匹配的数组 , n是字符串总长度 , m是字符范围 
    int i , j , p;
    int *x = wa , *y = wb , *t;
    for(i = 0 ; i < m ; i ++) wd[i] = 0;   
    for(i = 0 ; i < n ; i ++) wd[x[i]=r[i]] ++;
    for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1];
    for(i = n-1 ; i >= 0 ; i --) sa[-- wd[x[i]]] = i;/*****基排*****/
    for(j = 1 , p = 1 ; p < n ; j*=2 , m = p) {
        for(p = 0 , i = n - j ; i < n ; i ++) y[p++] = i;
        for(i = 0 ; i < n ; i ++) if(sa[i] >= j) y[p++] = sa[i] - j;/*****第二关键字排序*****/
        for(i = 0 ; i < n ; i ++) wv[i] = x[y[i]];
        for(i = 0 ; i < m ; i ++) wd[i] = 0;    
        for(i = 0 ; i < n ; i ++) wd[wv[i]] ++;
        for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1];
        for(i = n - 1 ; i >= 0 ; i --) sa[-- wd[wv[i]]] = y[i];/*****第一关键字排序*****/
        for(t = x , x = y , y = t , p = 1 , x[sa[0]] = 0 , i = 1 ; i < n ; i ++) {
            x[sa[i]] = cmp(y , sa[i-1] , sa[i] , j) ? p - 1: p ++;   
        }
    } 
}
void calheight(int *r , int n) {               //求height数组 
    int i , j , k = 0;
    for(i = 1 ; i <= n ; i ++) rank[sa[i]] = i;
    for(i = 0 ; i < n ; height[rank[i++]] = k) {
        for(k ? k -- : 0 , j = sa[rank[i]-1] ; r[i+k] == r[j+k] ; k ++);  
    }    
}
int main() {
    char str[maxn];
    int len1 , len2 , i , m , n , ans;
    while(scanf("%s",str)!=EOF) {
        len1 = strlen(str);
        for(i = 0 ; i < len1 ; i ++)
            num[i] = str[i] - 'a' + 2;
        num[len1] = ' ';
        scanf("%s",str);
        len2 = strlen(str);
        for(i = len1+1 ; i <= len1+len2 ; i ++) 
            num[i] = str[i - (len1+1)] - 'a' + 2;
        n = len1+len2+1;
        num[n] = 0;           //给这个位置赋个值 , 去掉也能过。。。 
        m = 50;
        ans = 0;
        for(i = 0 ; i < n ; i ++)
        printf("%d ",num[i]);    
        da(num , n+1 , m);//因为函数中是"< n" 所以n应该等于len1+len2+2 
        calheight(num , n);
        for(i = 2 ; i <= n ; i ++) {
          //  printf("%d\n",height[i]);
            if((sa[i]>len1&&sa[i-1]<len1)||(sa[i]<len1&&sa[i-1]>len1))
            ans = max(ans , height[i]);    
        }
        printf("%d\n",ans);
    }        
}


POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值