题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3971
题目大意:给定一个字符串S和一个重复次数n,求重复次数大等于n的最长子串,并输出最长子串中下标最大的那个子串的下标。
解题思路:本题可用后缀数组解决。先用倍增算法算出sa数组、rank数组再算出height数组,最后二分查找0-n的长度,找一个最长的长度符合重复数量大于k。做完做题能想到height数组中保存的最长公共前缀是像山一样的波浪形,有相同前缀的会扎堆。从某个位置开始,后面连续的height值都比它大的话,后面的值就和它有相同的公共前缀。那说到这里,本题的解法就自然而然出来,算到某个位置时它的height值如果大于mid,那向后查找连续的x位都比mid大,如果x大等于k,那就符合情况。
但本题比较麻烦的是输出最长最长重复子串的最大下标,三步走,1、记录找到的重复次数大于n的后缀中的最大下标 2、记录二分当前长度下大最大下标 3、直接更新到最终答案。当n为1时还需要特判。
测试数据:
1
aaabbbccc
baaaababababbababbab
aaabbbccc
cccccc
代码:
#include <stdio.h> #include <string.h> #define MAX 110000 char brr[MAX]; int n,renum,arr[MAX],st; int wn[MAX],wv[MAX],wa[MAX],wb[MAX]; int sa[MAX],rank[MAX],h[MAX]; 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) { int i,j,k,p; int *x = wa,*y = wb,*t; for (i = 0; i < m; ++i) wn[i] = 0; for (i = 0; i < n; ++i) wn[x[i]=r[i]]++; for (i = 1; i < m; ++i) wn[i] += wn[i-1]; for (i = n - 1; i >= 0; --i) sa[--wn[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) wn[i] = 0; for (i = 0; i < n; ++i) wn[wv[i]]++; for (i = 1; i < m; ++i) wn[i] += wn[i-1]; for (i = n - 1; i >= 0; --i) sa[--wn[wv[i]]] = y[i]; t = x,x = y,y = t,p = 1; for (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) { int i,j,k = 0; for (i = 1; i <= n; ++i) rank[sa[i]] = i; for (i = 0; i < n; h[rank[i++]] = k) for (k ? k-- : 0,j = sa[rank[i]-1];r[i+k] == r[j+k]; k++); } int Solve() { int i,j,k,tot,tp; int flag,maxx = 0; int low,high,mid,maxi; low = 1,high = n; while (low <= high) { tp = maxi = flag = 0; mid = low + (high - low) / 2; for (tot = 1,i = 1; i <= n; ++i) { //i是排名 if (h[i] >= mid) { tot++; if ( sa[i] > tp) tp = sa[i]; } else tot = 1,tp = sa[i]; if (tot >= renum) { flag = 1; if (tp > maxi) maxi = tp; } } if (flag) { low = mid + 1; maxx = mid; st = maxi; } else high = mid - 1; } return maxx; } int main() { int i,j,k,ans; while (scanf("%d",&renum),renum) { memset(arr,0,sizeof(arr)); scanf("%s",brr); if (renum == 1) { printf("%d %d\n",strlen(brr),0); continue; } for (i = 0; brr[i]; ++i) arr[i] = brr[i]; n = i,arr[n] = 0; Da(arr,n+1,MAX); CalHeight(arr,n); st = 0; ans = Solve(); if (ans == 0) printf("none\n"); else printf("%d %d\n",ans,st); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。