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 后缀数组)的更多相关文章
- acdream1116 Gao the string!(扩展KMP)
今天是字符串填坑的一天,首先填的第一个坑是扩展KMP.总结一下KMP和扩展KMP的区别. 在这里s是主串,t是模式串. KMP可以求出的是以s[i]为结尾的串和 t前缀匹配的最长的长度.假如这个长度是 ...
- BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )
二分答案m, 后缀数组求出height数组后分组来判断. ------------------------------------------------------------ #include&l ...
- bzoj 4310 跳蚤 二分答案+后缀数组/后缀树
题目大意 给定\(k\)和长度\(\le10^5\)的串S 把串分成不超过\(k\)个子串,然后对于每个子串\(s\),他会从\(s\)的所有子串中选择字典序最大的那一个,并在选出来的\(k\)个子串 ...
- BZOJ3796 Mushroom追妹纸(二分答案+后缀数组+KMP)
求出一个串使得这个串是\(s1,s2\)的子串.串中不包含\(s3\). 如果没有这个\(s3\)就可以二分答案,然后height小于二分值分一组.看看每组里是不是出现过\(s1,s2\)的后缀.判断 ...
- poj 1743 二分答案+后缀数组 求不重叠的最长重复子串
题意:给出一串序列,求最长的theme长度 (theme:完全重叠的子序列,如1 2 3和1 2 3 or 子序列中每个元素对应的差相等,如1 2 3和7 8 9) 要是没有差相等这个条件那就好办 ...
- 2018.11.24 poj1743Musical Theme(二分答案+后缀数组)
传送门 代码: 二分答案. 然后对于预处理的heightheightheight数组分成几段. 保证每一段中都是连续的几个heightheightheight并且这些heightheightheigh ...
- [BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1360 Solved: 545[S ...
- poj 3261 二分答案+后缀数组 求至少出现k次的最长重复子序列
#include "stdio.h" #define maxn 20010 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; int rank[ma ...
- 2018.11.24 poj3261Milk Patterns(后缀数组)
传送门 后缀数组经典题. 貌似可以用二分答案+后缀数组? 我自己yyyyyy了一个好写一点的方法. 直接先预处理出heightheightheight数组. 然后对于所有连续的k−1k-1k−1个he ...
随机推荐
- ListView用法及加载数据时的闪烁问题和加载数据过慢问题
ListView介绍及添加数据时的闪烁问题 1. ListView类 1.1 ListView常用的基本属性: (1)FullRowSelect:设置是否行选择模式.(默认为false) 提示 ...
- public、private、protected 与 默认 等访问修饰符
1)public(公共的):被public修饰的属性和方法可以被所有类访问. 2)private(私有的):被private修饰的属性和方法只能在改类的内部使用. 3)protected(受保护的): ...
- 使用eclipse开发
Eclipse下载地址:http://www.eclipse.org/ 下载后进行解压缩,点击eclipse.exe即可使用eclipse workspace:工作区 Project:项目 ...
- 刀哥多线程之gcd-01-sync&async
同步 & 异步 概念 同步 必须等待当前语句执行完毕,才会执行下一条语句 异步 不用等待当前语句执行完毕,就可以执行下一条语句 NSThread 中的 同步 & 异步 - (void) ...
- Java中HashMap,LinkedHashMap,TreeMap的区别[转]
原文:http://blog.csdn.net/xiyuan1999/article/details/6198394 java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类 ...
- Stream,Reader/Writer,Buffered的区别(1)
Stream: 是字节流形式,exe文件,图片,视频等.支持8位的字符,用于 ASCII 字符和二进制数据. Reader/Writer: 是字符流,文本文件,XML,txt等,用于16位字符,也就是 ...
- Node.js深受欢迎的六大原因
Node.js是一种后起的优秀服务器编程语言,它用来构建和运行Web应用,这和ASP.NET,Ruby on Rails或Spring框架做的工作是类似的.它使用JavaScript作为主要的开发语言 ...
- 【EF Code First】Migrations数据库迁移
1,打开工具->NuGet程序管理器->程序包管理器控制台 默认项目中要选择 数据访问上下文类 所在的项目 我的DB是在命名空间CodeFirst.UI下的所以选择CodeFirst. ...
- 49.关于Quartus和ISE中ROM的初始化和仿真的一些小结
最近在玩Altera的FPGA,当我用Quartus II自带的IP核生成ROM时,出现了各种问题,于是在网上各种查资料,终于解决了我的问题.这里做一下小结,方便自己日后查阅. Quartus II ...
- unity工具IGamesTools之批量生成帧动画
unity工具IGamesTools批量生成帧动画,可批量的将指定文件夹下的帧动画图片自动生成对应的资源文件(Animation,AnimationController,Prefabs) unity工 ...