砖提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 源代码如下:

#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 源代码如下:

#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 源代码如下:

#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 源代码如下:

#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)

#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 源代码如下

#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 源代码如下

#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. 阅读(271) 评论(1) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库