现在的位置: 首页 > 综合 > 正文

Zoj 3595 Stammering Aliens(字符串_后缀数组)

2013年10月26日 ⁄ 综合 ⁄ 共 2070字 ⁄ 字号 评论关闭

题目链接: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


3

baaaababababbababbab


3

aaabbbccc


3

cccccc


11
baaaababababbababbab


代码:

#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原创,但可以转载,因为我们是兄弟。

抱歉!评论已关闭.