书山有径勤为路>>>>>>>>

<<<<<<<<学海无涯苦作舟!

砖提1

线段树,后缀树(KMP,AC自动机),后缀数组,树状数组

1.树形dp

hdu 1011 题意http://www.cnblogs.com/183zyz/archive/2011/07/19/2110983.html

hdu 1011 题解http://acm.hdu.edu.cn/viewcode.php?rid=6445809

 

2.线段树

hdu 1166 题目http://acm.hdu.edu.cn/showproblem.php?pid=1166

hdu 1166 题解http://acm.hdu.edu.cn/viewcode.php?rid=6449415

 

3.后缀树

构造后缀树的详细算法描述 http://blog.163.com/lazy_p/blog/static/13510721620108139476816/ 

 

4.后缀数组

    首先 RMQ问题 详解和题目 http://blog.csdn.net/yuhailin060/article/details/5355823 

    对于详解我再做一点补充,在len要限制为2的t次幂时,我们要保证 2的t <= r

    看好了,两边取log log2的t = logr  <=>  tlog2 = logr  <=> t=logr/log2;

    RMQ poj 3264 我的题解http://poj.org/showsource?solution_id=10620677

    1.不可重叠最长重复子串:

    第一个懂了的后缀数组题目 poj 1743  题目http://poj.org/problem?id=1743

    第一个懂了的后缀数组题目 poj 1743  题意http://www.iteye.com/blogs/tag/poj%201743

    第一个懂了的后缀数组题目 poj 1743  源代码如下:

View Code
#include "stdio.h"
#define maxn 20000

int wa[maxn],wb[maxn],wv[maxn],ws[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 *sa,int n,int m)
{
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[x[i]=r[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) sa[--ws[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++) ws[i]=0;
       for(i=0;i<n;i++) ws[wv[i]]++;
       for(i=1;i<m;i++) ws[i]+=ws[i-1];
       for(i=n-1;i>=0;i--) sa[--ws[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++;
     }
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     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++);
     return;
}

int check(int *sa,int n,int k)
{
    int i,max=sa[1],min=sa[1];//排在第一位的后缀的起始位置
    for(i=2;i<=n;i++)
    {
      if(height[i]<k) max=min=sa[i]; //分成heigh[i]<k的一组
      else //分成height[i]>=k的一组
      {
        if(sa[i]<min) min=sa[i];
        if(sa[i]>max) max=sa[i];
        if(max-min>k) return(1); //由于不可以重叠,所以两个后缀的其实位置的差要大于k      
}
    }
    return(0);
}
int r[maxn],sa[maxn];
int main()
{
    int i,j=0,k,n;
    int min,mid,max;
    scanf("%d",&n);
    while(n!=0)
    {
      n--;
      scanf("%d",&j);
      for(i=0;i<n;i++)
      {
        scanf("%d",&k);
        r[i]=k-j+100;
        j=k;
      }
      r[n]=0;
      da(r,sa,n+1,200);
      calheight(r,sa,n);
      min=1;max=n/2;// 二分的数值范围是1~n/2
      while(min<=max)
      {
        mid=(min+max)/2;
        if(check(sa,n,mid)) min=mid+1;
        else max=mid-1;
      }
      if(max>=4) printf("%d\n",max+1);
      else printf("0\n");
      scanf("%d",&n);
    }
    return 0;
}

   

   2.可重叠k次的最长子串(至少重叠k次)

    第二个懂了的后缀数组题目 poj 3261  题目http://poj.org/problem?id=3261 

    第二个懂了的后缀数组题目 poj 3261  源代码如下:

View Code
#include<stdio.h>
#define maxn 20001
#define maxm 1000002

int wa[maxn],wb[maxn],wv[maxn],ws[maxm];
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 *sa,int n,int m)
{
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[x[i]=r[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) sa[--ws[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++) ws[i]=0;
       for(i=0;i<n;i++) ws[wv[i]]++;
       for(i=1;i<m;i++) ws[i]+=ws[i-1];
       for(i=n-1;i>=0;i--) sa[--ws[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++;
     }
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     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++);
     return;
}

int check(int n, int k, int mid)
{
    int i, Cnt = 1;//因为每找到一个height[i]就表示两个后缀的比较结果,所以Cnt=1,不是Cnt=0
    for(i=2; i<=n; i++)
    {
        if(height[i]>=mid)//在同一组,累加
        {
            Cnt++;
            if(Cnt>=k) return 1;
        }
        else Cnt =1;//转到了另外一组,那么要将Cnt值为1
    }
    return 0;
}
int r[maxn], sa[maxn];
int main()
{
    int i, j, k, n;
    int min, max, mid;
    while(scanf("%d%d", &n, &k)!=EOF)
    {
        for(i=0; i<n; i++)
        {
            scanf("%d", &r[i]);
            r[i]++;
        }
        r[n] = 0;
        da(r, sa, n+1, maxm);
        calheight(r, sa, n);
        min = 1; max = n;
        while(min<=max)
        {
            mid = (mid+max)/2;
            if(check(n, k, mid)) min = mid+1;
            else max = mid-1;
        }
        printf("%d\n", max);
    }
    return 0;
}

    

     3.不相同的子串个数

     第三个懂了的后缀数组题目 spoj 694  题目http://www.spoj.pl/problems/DISUBSTR/

     第三个懂了的后缀数组题目 spoj 694  源代码如下:

View Code
#include "stdio.h"
#include "string.h"
#define maxn 50001

int wa[maxn],wb[maxn],wv[maxn],ws[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 *sa,int n,int m)
{
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[x[i]=r[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) sa[--ws[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++) ws[i]=0;
       for(i=0;i<n;i++) ws[wv[i]]++;
       for(i=1;i<m;i++) ws[i]+=ws[i-1];
       for(i=n-1;i>=0;i--) sa[--ws[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++;
     }
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     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++);
     return;
}

char s[maxn];
int r[maxn],sa[maxn];
int main()
{
    int i,n,t;
    long long ans;
    scanf("%d",&t);
    while(t-->0)
    {
      scanf("%s",s);
      n=strlen(s);
      for(i=0;i<n;i++) r[i]=s[i];
      r[n]=0;
      da(r,sa,n+1,128);
      calheight(r,sa,n);
      ans=(long long)n*(n+1)/2;
      for(i=1;i<=n;i++) ans-=height[i];
      printf("%lld\n",ans);
    }
    return 0;
}

 

     4.最长回文子串

   (求两个后缀的最长公共前缀:就是求height[]中这两个后缀区间的最小值)

     第四个懂了的后缀数组题目 ural 1297 题目http://acm.timus.ru/problem.aspx?space=1&num=1297

     第四个懂了的后缀数组题目 ural 1297 源代码如下:

View Code
#include "stdio.h"
#include "string.h"
#include "math.h"
#define maxn 2002

int wa[maxn],wb[maxn],wv[maxn],ws[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 *sa,int n,int m)
{
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[x[i]=r[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) sa[--ws[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++) ws[i]=0;
       for(i=0;i<n;i++) ws[wv[i]]++;
       for(i=1;i<m;i++) ws[i]+=ws[i-1];
       for(i=n-1;i>=0;i--) sa[--ws[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++;
     }
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     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++);
     return;
}
int RMQ[maxn];
int best[maxn][20];
void initRMQ(int n)
{
     int i,j,a,b;
     int l=int(log(double(n))/log(2.0));
     for(i=1;i<=n;i++) best[i][0]=i;
     for(i=1;i<=l;i++)
     for(j=1;j<=n+1-(1<<i);j++)
     {
       a=best[j][i-1];
       b=best[j+(1<<(i-1))][i-1];
       if(RMQ[a]<RMQ[b]) best[j][i]=a;//height[]值较小的才是最长公共前缀,但是记录的是排序后的位置
       else best[j][i]=b;
     }
     return;
}
int askRMQ(int a,int b)
{
    int t=int(log(double(b-a+1))/log(2.0));
    a=best[a][t];b=best[b-(1<<t)+1][t];
    return RMQ[a]<RMQ[b]?a:b;//返回a,b这个区间段的最长公共前缀在排序中的位置
}
int lcp(int a,int b)//获得最长公共前缀的长度
{
    int t;
    a=rank[a];b=rank[b];//分别获得后缀su[a], su[b]在排序中的位置
    if(a>b) {t=a;a=b;b=t;}
    return(height[askRMQ(a+1,b)]);//得到最长公共前缀的长度
}

char st[maxn];
int r[maxn],sa[maxn];
int main()
{
    int i,n,len,k,ans=0,w;
    scanf("%s",st);
    len=strlen(st);
    for(i=0;i<len;i++) r[i]=st[i];
    r[len]=1;
    for(i=0;i<len;i++) r[i+len+1]=st[len-1-i];
    n=len+len+1;
    r[n]=0;
    da(r,sa,n+1,128);
    calheight(r,sa,n);
    for(i=1;i<=n;i++) RMQ[i]=height[i];
    initRMQ(n);
    for(i=0;i<len;i++)
    {
      k=lcp(i,n-i);//奇数或偶数
      if(k*2>ans) ans=k*2,w=i-k; //ans保存最长公共前缀的结果,w保存最长公共前缀在字符串的其实位置

      k=lcp(i,n-i-1);//偶数或奇数
      if(k*2-1>ans) ans=k*2-1,w=i-k+1;
    }
    st[w+ans]=0;
    printf("%s\n",st+w);
    return 0;
}

 

    5.连续重复子串

     第五个懂了的后缀数组题目 poj 2406 题目http://poj.org/problem?id=2406

     第五个懂了的后缀数组题目 poj 2406 源代码如下:(超时,没办法这个题目卡的就是后缀数组,TMD)

View Code
#include "math.h"
#include<stdio.h>
#include<string.h>
#define maxn 1000005
#define maxm 300
char s[maxn];
int r[maxn], sa[maxn];
int wa[maxn],wb[maxn],wv[maxn],ws[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 *sa,int n,int m)
{
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[x[i]=r[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) sa[--ws[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++) ws[i]=0;
       for(i=0;i<n;i++) ws[wv[i]]++;
       for(i=1;i<m;i++) ws[i]+=ws[i-1];
       for(i=n-1;i>=0;i--) sa[--ws[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++;
     }
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
     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++);
     return;
}
int RMQ[maxn];
int best[maxn][20];
void initRMQ(int n)
{
     int i,j,a,b;
     int l=int(log(double(n))/log(2.0));
     for(i=1;i<=n;i++) best[i][0]=i;
     for(i=1;i<=l;i++)
     for(j=1;j<=n+1-(1<<i);j++)
     {
       a=best[j][i-1];
       b=best[j+(1<<(i-1))][i-1];
       if(RMQ[a]<RMQ[b]) best[j][i]=a;//height[]值较小的才是最长公共前缀,但是记录的是排序后的位置
       else best[j][i]=b;
     }
     return;
}
int askRMQ(int a,int b)
{
    int t=int(log(double(b-a+1))/log(2.0));
    a=best[a][t];b=best[b-(1<<t)+1][t];
    return RMQ[a]<RMQ[b]?a:b;//返回a,b这个区间段的最长公共前缀在排序中的位置
}
int lcp(int a,int b)//获得最长公共前缀的长度
{
    int t;
    a=rank[a];b=rank[b];//分别获得后缀su[a], su[b]在排序中的位置
    if(a>b) {t=a;a=b;b=t;}
    return(height[askRMQ(a+1,b)]);//得到最长公共前缀的长度
}
void getans(int n)
{
    int i, ans;
    for(i=n;i>0; i--)
    {
        
        if(n%i==0 && lcp(0, i)==n-i) ans=n/i;
    }
    printf("%d\n", ans);
}

int main()
{
    int i, n, t;
    while(~scanf("%s", s))
    {
        if(strcmp(s, ".")==0) break;
        n=strlen(s);
        for(i=0; i<n; i++) r[i]=s[i];
        r[n]=0;
        da(r, sa, n+1, 300);
        calheight(r, sa, n);
        for(i=1;i<=n;i++) RMQ[i]=height[i];
        initRMQ(n);
        getans(n);
    }
    return 0;
}

 

5.KMP问题

    kmp算法详解:http://bbezxcy.iteye.com/blog/1355293

    基础kmp题目 poj 3461 http://poj.org/problem?id=3461

    poj 3461 源代码如下

View Code
#include "iostream"
#include "string"
#include "algorithm"
using namespace std;
char a[1000010], b[10010];
int p[11111];
int n, m;
void getp()
{
    p[1] = 0;
    int i, j=0;
    for(i=2; i<=m; i++)
    {
        while(j>0 && b[j+1]!=b[i]) j=p[j];
        if(b[j+1]==b[i]) j+=1;
        p[i]=j;
    }
}
void kmp()
{
    int i, j=0, cnt=0;
    for(i=1; i<=n; i++)
    {
        while(j>0 && b[j+1]!=a[i]) j=p[j];//不相等时,j往前返回
        if(b[j+1]==a[i]) j+=1; //相等时j加1
        if(j==m)
        {
            cnt++;
            j=p[j]; //j返回,重新再来匹配
        }
    }
    printf("%d\n", cnt);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>(b+1)>>(a+1);
        m=strlen(b+1);
        n=strlen(a+1);
        getp();
        kmp();
    }
}

    

    kmp理解型题目 poj 2406 http://poj.org/problem?id=2406

    poj 2406 源代码如下

View Code
#include "iostream"
#include "string"
#include "algorithm"
using namespace std;
char b[1000010];
int p[1000010];
int m;
void getp()
{
    p[1] = 0;
    int i, j=0;
    for(i=2; i<=m; i++)
    {
        while(j>0 && b[j+1]!=b[i]) j=p[j];
        if(b[j+1]==b[i]) j+=1;
        p[i]=j;
    }
}
int main()
{
    while(cin>>(b+1))
    {        
        if(strcmp(b+1,".")==0) break;
        m=strlen(b+1);
        getp();
        if(0==m%(m-p[m])) printf("%d\n", m/(m-p[m]));
        else printf("1\n");
    }
}

 

 

posted on 2012-08-05 09:35  More study needed.  阅读(270)  评论(1编辑  收藏  举报

导航

书山有径勤为路>>>>>>>>

<<<<<<<<学海无涯苦作舟!