码迷,mamicode.com
首页 > 其他好文 > 详细

acdream1116 Gao the string!(hash二分 or 后缀数组)

时间:2014-07-13 21:50:25      阅读:235      评论:0      收藏:0      [点我收藏+]

标签:blog   os   问题   for   io   div   

问题套了一个斐波那契数,归根结底就是要求对于所有后缀s[i...n-1],所有前缀在其中出现的总次数。我一开始做的时候想了好久,后来看了别人的解法才恍然大悟。对于一个后缀来说 s[i...n-1]来说,所有与它匹配的前缀必然是和 s[i+1...n-1]  s[i+2...n-1] ....s[n-1..n-1]里的前缀匹配的,因而如果我们定义一个num[i]表示的是后缀s[i...n-1]与前缀的总长公共前缀,那么num[i]+num[i+1]+..num[n-1]就是前缀在后缀i里出现的次数总和,所以问题转化为求s[i...n-1]跟前缀的最长公共前缀,也可以理解成是  s[0...n-1]和s[i...n-1]求最长公共前缀。

求后缀的最长公共前缀,一个做法是后缀数组,然后建lcp,然后通过rmq询问,理论的复杂度nlogn,我自己套的模板用的是快排,所以后缀数组本身的复杂度就已经是nlog^2n,然后我就愉快的TLE了。

失败之后就只能转而用hash+二分,毕竟二分还是慢,所以我卡着时限过了这题,心有余悸。

#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <string>
#include <algorithm>
using namespace std;

#define maxn 110000
#define ll long long
#define mod 1000000007
#define step 31

/*int rk[maxn], sa[maxn], lcp[maxn];
int tmp[maxn];
int d[maxn + 50][25];*/

int n;

/*bool cmp_sa(int i, int j)
{
	if (rk[i] != rk[j]) return rk[i] < rk[j];
	else{
		int ri = i + k <= n ? rk[i + k] : -1;
		int rj = j + k <= n ? rk[j + k] : -1;
		return ri < rj;
	}
}

void construct_sa(char *s, int *sa)
{
	n = strlen(s);
	for (int i = 0; i <= n; i++){
		sa[i] = i;
		rk[i] = i < n ? s[i] : -1;
	}
	for (k = 1; k <= n; k <<= 1){
		sort(sa, sa + n + 1, cmp_sa);
		tmp[sa[0]] = 0;
		for (int i = 1; i <= n; i++){
			tmp[sa[i]] = tmp[sa[i - 1]] + (cmp_sa(sa[i - 1], sa[i]) ? 1 : 0);
		}
		for (int i = 0; i <= n; i++){
			rk[i] = tmp[i];
		}
	}
}

void construct_lcp(char *s, int *sa, int *lcp)
{
	n = strlen(s);
	for (int i = 0; i <= n; i++) rk[sa[i]] = i;
	int h = 0;
	lcp[0] = 0;
	for (int i = 0; i < n; i++){
		int j = sa[rk[i] - 1];
		for (h ? h-- : 0; i + h < n&&j + h < n&&s[i + h] == s[j + h]; h++);
		lcp[rk[i] - 1] = h;
	}
}

void construct_rmq(int *lcp, int sizen)
{
	for (int i = 0; i <= sizen; i++) d[i][0] = lcp[i];
	for (int j = 1; (1 << j) <= sizen; j++){
		for (int i = 0; (i + (1 << j) - 1) <= sizen; i++){
			d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
		}
	}
}

int rmq_query(int l, int r)
{
	if (l > r) swap(l, r); r -= 1;
	int k = 0; int len = r - l + 1;
	while ((1 << (k + 1)) < len) k++;
	return min(d[l][k], d[r - (1 << k) + 1][k]);
}

*/

char s[maxn];
ll num[maxn];

struct Matrix
{
	ll a[2][2];
	Matrix(){ memset(a, 0, sizeof(a)); }
}m;

Matrix operator * (const Matrix &a,const Matrix &b){
	Matrix ret;
	for (int i = 0; i < 2; i++){
		for (int j = 0; j < 2; j++){
			for (int k = 0; k < 2; k++){
				ret.a[i][j] += (a.a[i][k] * b.a[k][j]) % mod;
				ret.a[i][j] %= mod;
			}
		}
	}
	return ret;
}
Matrix operator ^ (Matrix a,ll n){
	Matrix ret;
	for (int i = 0; i < 2; i++) ret.a[i][i] = 1;
	while (n){
		if (n & 1) ret = ret*a;
		n >>= 1;
		a = a*a;
	}
	return ret;
}

ll cal(ll n)
{
	m.a[0][0] = 0; m.a[0][1] = 1;
	m.a[1][0] = 1; m.a[1][1] = 1;
	m = m^n;
	return m.a[0][1];
}

ll seed[maxn];
ll h[maxn];

ll get(int l, int r){
	return ((h[r] - h[l - 1] * seed[r - l + 1]%mod) + mod) % mod;
}


int getlcp(int x)
{
	if (get(1, 1) != get(x, x)) return 0;
	int l = 1,r = n - x + 1;
	while (l <= r){
		int m = (l + r) >> 1;
		if (get(1, 1 + m - 1) == get(x, x + m - 1)) l = m+1;
		else r = m-1;
	}
	return r;
}


int main()
{
	seed[0] = 1;
	for (int i = 1; i < maxn; i++){
		seed[i] = seed[i - 1] * step%mod;
	}
	while (~scanf("%s", s + 1))
	{
		n = strlen(s + 1); h[0] = 0;
		for (int i = 1; i <= n; i++){
			h[i] = (h[i - 1] * step + s[i]) % mod;
		}
		num[1] = n;
		for (int i = 2; i <= n; i++){
			num[i] = getlcp(i);
		}
		ll ans = 0;
		num[n + 1] = 0;
		for (int i = n; i >= 1; i--){
			num[i] += num[i + 1];
			ans += cal(num[i]);
			ans %= mod;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

 

acdream1116 Gao the string!(hash二分 or 后缀数组),布布扣,bubuko.com

acdream1116 Gao the string!(hash二分 or 后缀数组)

标签:blog   os   问题   for   io   div   

原文地址:http://www.cnblogs.com/chanme/p/3840671.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!