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

问题套了一个斐波那契数,归根结底就是要求对于所有后缀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 后缀数组)

时间: 2024-10-07 18:42:12

acdream1116 Gao the string!(hash二分 or 后缀数组)的相关文章

acdream1116 Gao the string!(扩展KMP)

今天是字符串填坑的一天,首先填的第一个坑是扩展KMP.总结一下KMP和扩展KMP的区别. 在这里s是主串,t是模式串. KMP可以求出的是以s[i]为结尾的串和 t前缀匹配的最长的长度.假如这个长度是L的话,则: s[i-L+1...i]=t[0...L] 而所谓的失配指针f[i]指的就是当前i点失配时要匹配的长度,实际是用t文本串去匹配t. 扩展KMP则是以s[i]为起始的串和 t前缀匹配的最长的长度. 假如这个长度的话,则: s[i..i+L-1]=t[0...L] 扩展KMP里的nxt数组

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树) 题面 给出一个长度为\(n\)的字符串\(s\),以及\(m\)组询问.每个询问是一个四元组\((a,b,c,d)\),问\(s[a,b]\)的所有子串和字符串\(s[c,d]\)的最长公共前缀长度的最大值. \(n,m \leq 10^5\) 分析 显然答案有单调性.首先我们二分答案\(mid\),考虑如何判定. 如果mid这个答案可行,那么一定存在一个后缀x,它的开头在\([a,

BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )

二分答案m, 后缀数组求出height数组后分组来判断. ------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; const int maxn = 20009; struct HASH { int id[maxn], N; HASH() { N = 0; } inline void work() { sort(id, id + N); N

BZOJ 3790 神奇项链 Hash+二分+树状数组

题目大意:给定一个串,问这个串最少可以由回文串拼接多少次而成(拼接可以重叠) 首先将每两个字符之间插入占位符,然后Hash+二分搞出所有极大回文串(可以用manacher,我不会) 问题转化成了给定一些区间,求最少的能覆盖整个数轴的区间 将所有区间按照某一端点排序 然后上树状数组即可 回头还是去学学manacher吧... #include <cstdio> #include <cstring> #include <iostream> #include <algo

poj 1743 二分答案+后缀数组 求不重叠的最长重复子串

题意:给出一串序列,求最长的theme长度 (theme:完全重叠的子序列,如1 2 3和1 2 3  or  子序列中每个元素对应的差相等,如1 2 3和7 8 9) 要是没有差相等这个条件那就好办多了,直接裸题. 一开始想了个2B方法,后来发现真心2B啊蛤蛤蛤 1 for i=1 to 88 do 2 { 3 for j=1 to length 4 { 5 r2[j]=r[j]+i; 6 if (r2[j]>88) r2[i]-=88; 7 } 8 把新序列r2连接到原序列r的后面 9 pr

[BZOJ4556][TJOI2016&amp;&amp;HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)

4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1360  Solved: 545[Submit][Status][Discuss] Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE O,嫁给高富帅,走上人生巅

poj 3261 二分答案+后缀数组 求至少出现k次的最长重复子序列

1 #include "stdio.h" 2 #define maxn 20010 3 4 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; 5 int rank[maxn],height[maxn]; 6 int r[maxn],sa[maxn],ans[maxn]; 7 int n,res,k; 8 9 int cmp(int *r,int a,int b,int l) 10 { 11 return r[a]==r[b]&&r[a+l

Acdreamoj1116(Gao the string!)字符串hash+二分+矩阵快速幂

Problem Description give you a string, please output the result of the following function mod 1000000007 n is the length of the string f() is the function of fibonacci, f(0) = 0, f(1) = 1... a[i] is the total number of times any prefix appear in the

bnuoj 34990(后缀数组 或 hash+二分)

后缀数组倍增算法超时,听说用3DC可以勉强过,不愿写了,直接用hash+二分求出log(n)的时间查询两个字符串之间的任意两个位置的最长前缀. 我自己在想hash的时候一直在考虑hash成数值时MOD取多大,如果取10^18的话,那么两数相乘个就超LL了,但是取10^9的话又怕出现重复的可能大.后面才发现自己是sb,如果用unsigned long long 如果有溢出或者为负数是直接变成对(1<<64)取模了. 也就是无符号长整形运算自动帮你取模了.所以可以放心用hash Justice S