POJ 1743 Musical Theme SA

本文介绍了解决POJ1743问题的方法,通过二分查找与最长不重复子串的算法实现,详细解释了如何利用SA-IS算法进行字符串处理,并给出了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

POJ 1743

Sample Input

30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
0

Sample Output

5

题意 给你n个乐符 让你差分以后找到最长不重复连续子串

做法 我们首先不能枚举答案 而是要二分

需要用height数组把大于二分时的mid值的数据放在一组 然后统计 其中sa的最大值和最小值 如果maxx-minn > mid 那么就是满足不重叠了

为什么要在一组呢?因为我们知道height数组是lcp数组 如果你想要最长公共连续子串 那么一定相邻的height数组里面找到这个最长公共连续子串的 注意二分上届应该是n+1 不然会出错

/*
    if you can't see the repay
    Why not just work step by step
    rubbish is relaxed
    to ljq
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;

#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))

typedef pair<int,int> pll;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int _inf = 0xc0c0c0c0;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll _INF = 0xc0c0c0c0c0c0c0c0;
const ll mod =  (int)1e9+7;

ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll ksm(ll a,ll b,ll mod){int ans=1;while(b){if(b&1) ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
ll inv2(ll a,ll mod){return ksm(a,mod-2,mod);}
const int MAX_N = 3e4+5;
int wa[MAX_N],wb[MAX_N],wsf[MAX_N],wv[MAX_N],sa[MAX_N],n;
int Rank[MAX_N],height[MAX_N],s[MAX_N];
int str1[MAX_N],str2[MAX_N];
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void getsa(int *r,int *sa,int n,int m)//n为添加0后的总长
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++)  wsf[i]=0;
    for(i=0; i<=n; i++)  wsf[x[i]=r[i]]++;
    for(i=1; i<m; i++)  wsf[i]+=wsf[i-1];
    for(i=n; i>=0; i--)  sa[--wsf[x[i]]]=i;
    p=1;
    j=1;
    for(; p<=n; j*=2,m=p)
    {
        for(p=0,i=n+1-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++)  wsf[i]=0;
        for(i=0; i<=n; i++)  wsf[wv[i]]++;
        for(i=1; i<m; i++)  wsf[i]+=wsf[i-1];
        for(i=n; i>=0; i--)  sa[--wsf[wv[i]]]=y[i];
        t=x;
        x=y;
        y=t;
        x[sa[0]]=0;
        for(p=1,i=1; i<=n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
    }
}

void getheight(int *r,int n)//n为添加0后的总长
{
    int i,j,k=0;
    for(i=1; i<=n; i++)  Rank[sa[i]]=i;
    for(i=0; i<n; i++)
    {
        if(k)
            k--;
        else
            k=0;
        j=sa[Rank[i]-1];
        while(r[i+k]==r[j+k])
            k++;
        height[Rank[i]]=k;
    }
}

int main()
{
    //ios::sync_with_stdio(false);
    //freopen("a.txt","r",stdin);
    //freopen("b.txt","w",stdout);
    int t;
    while(scanf("%d",&t)&&t)
    {
        n = 0;
        for(int i = 1;i<=t;++i)
            scanf("%d",&str1[i]);
        for(int i = 2;i<=t;++i)
            s[n++] = str1[i] - str1[i-1] + 89;
        s[n] = 0;
        getsa(s,sa,n,190);
        getheight(s,n);
        int l = 0,r = n+1,maxx = -1,minn = 2e4+5;
        while(l<=r)
        {
            bool flag = false;
            int mid = (l+r)>>1;
            for(int i = 1;i<=n;++i)
            {
                if(height[i]>=mid)
                {
                    maxx = max(maxx,max(sa[i],sa[i-1]));
                    minn = min(minn,min(sa[i],sa[i-1]));
                }
                else
                {
                    if(maxx-minn >mid) flag = true;
                    minn = 2e4+5,maxx = -1;
                }
            }
            if(maxx-minn>mid) flag = true;
            if(flag) l = mid + 1;
            else r = mid - 1;
        }
        if(r<4) printf("0\n");
        else printf("%d\n",r+1);
    }
    //fclose(stdin);
    //fclose(stdout);
    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值