ACdreamOJ 1116 斐波那契数列+hash计算后缀

http://acdream.info/problem?pid=1116

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 suffix s[i....n-1].

(the prefix means s[0...i] )

Input

multiple test cases.

each test case will give you a string consists of lowercase letters, the length of which is no more than 100000.

Output

ouput a number.

Sample Input

aa

Sample Output

3

Hint

样例解释如下:

对于

aa这个后缀,前缀a出现了两次,前缀aa出现了一次,总共三次

对于a这个后缀,前缀a出现了一次

所以答案是f(3) + f(1)

哈希的讲解:刘汝佳大白书P224.基于哈希值的LCP算法

/**
ACdreamOJ 1116 (hash 斐波那契数列+hash)
题目大意:
        求f(num[i])(i:0~len-1)的和。num[i]表示所有的前缀在以i为起点的后缀中出现的次数。
解题思路:
        如果已知num[i],那么利用矩阵连乘就可以求出f(num[i]),因此本题的关键是求出num[i].
        利用hash算法,用has[i]表示以i开头的后缀的哈希值;(hash(i,L)=has(i)-has(i+L)*Hash[L]):表示字符串s[i]~s[i+L]的哈希值.
        利用二分求出num1[i](以i为起点的后缀的字符串和原字符串公共前缀的数目)进而num1[]的后缀和就是num[].
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long LL;
const int seed=31;
const int maxn=100010;
///============== 斐波那契数列求法=============
const int INF=1e9+7;
const int MAX=2;
struct Matrix
{
    LL m[MAX][MAX];
};
Matrix P= {0,1,1,1};
Matrix I= {1,0,0,1};
Matrix matrixmul(Matrix a,Matrix b)//、矩阵相乘
{
    Matrix c;
    for(int i=0; i<MAX; i++)
    {
        for(int j=0; j<MAX; j++)
        {
            c.m[i][j]=0;
            for(int k=0; k<MAX; k++)
                c.m[i][j]+=(a.m[i][k]*b.m[k][j])%INF;
            c.m[i][j]%=INF;
        }
    }
    return c;
}
Matrix quickpow(LL n)///矩阵快速幂
{
    Matrix m=P,b=I;
    while(n)
    {
        if(n&1)
            b=matrixmul(b,m);
        n>>=1;
        m=matrixmul(m,m);
    }
    return b;
}
LL getans(LL t)///返回f(t)的值
{
    if(t==0)
        return 0;
    if(t<2)
        return 1;
    Matrix A=quickpow(t-1);
    return A.m[0][0]+A.m[0][1];
}
///=========================================

///=============字符串后缀哈希值的计算======
LL Hash[maxn],has[maxn],num[maxn];
int len;
char s[maxn];

void init()///初始化
{
    Hash[0]=1;
    for(int i=1; i<maxn; i++)
        Hash[i]=Hash[i-1]*seed;
}

LL get_hash(int i,int L)///得到以i为起点长度为L的后缀的哈希值
{
    return has[i]-has[i+L]*Hash[L];
}

bool ok(int i,int l)
{
    return get_hash(0,l)==get_hash(i,l);
}

void getnum(int i)///二分,求以i为起点的后缀的字符串和原字符串公共前缀的数目
{
    int left=i,right=len-1;
    while(left<=right)
    {
        int mid=(left+right)/2;
        if(ok(i,mid-i+1))
            left=mid+1;
        else
            right=mid-1;
    }
    num[i]=right-i+1;
}

void makehash()///得到每个后缀的哈希值
{
    for(int i=len-1; i>=0; i--)
    {
        has[i]=has[i+1]*seed+s[i];
    }
    num[0]=len;
    for(int i=1; i<len; i++)
        getnum(i);
}

///===========================================

int main()
{
    init();

    /* int n;
    while(~scanf("%d",&n))
    cout << getans(n)<< endl;*/

    while(~scanf("%s",s))
    {
        memset(has,0,sizeof(has));
        len=strlen(s);
        makehash();
        for(int i=len-1; i>=0; i--)
            num[i]+=num[i+1];
        LL ans=0;
        for(int i=0; i<len; i++)
            ans=(ans+getans(num[i]))%INF;
        cout << ans << endl;
    }
    return 0;
}
时间: 2024-10-06 12:16:34

ACdreamOJ 1116 斐波那契数列+hash计算后缀的相关文章

斐波那契数列快速计算

感觉一天时间过得挺快,而自己却没有什么收获. 1.之前恰好看了跟快速幂乘法一样的计算大数乘法模,防止溢出,感觉挺有用的,而且用的挺多的. 2.分析问题的能力还很差,遇到一个问题,无法正确的进行转化,怎么进行考虑,感觉自己这方面还很欠缺,这应该是通过大量做题,然后不断总结得出来的吧!毕竟题做的多了,遇到新题也就那几种套路.感觉也是挺对的,面试题的那些小套路在搞竞赛的人面前根本什么也不是,感觉这句话挺有道理的. 3. 这次做的这道题,最后就是转化为求第n个斐波那契数,而我根本没有推导出这个.然后,之

斐波拉契数列加强版——时间复杂度O(1),空间复杂度O(1)

对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - 1) + F(n - 2),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围内的非负整数,请设计一个高效算法,计算第n项F(n).第一个斐波拉契数为F(0) = 1. 给定一个非负整数,请返回斐波拉契数列的第n项,为了防止溢出,请将结果Mod 1000000007. 斐波拉契数列的计算是一个非常经典的问题,对于小规模的n,很容易用递归的方式来获取,对于稍微大一点的n,为

面试官问你斐波那契数列的时候不要高兴得太早

前言 假如面试官让你编写求斐波那契数列的代码时,是不是心中暗喜?不就是递归么,早就会了.如果真这么想,那就危险了. 递归求斐波那契数列 递归,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法.斐波那契数列的计算表达式很简单: 1F(n) = n; n = 0,12F(n) = F(n-1) + F(n-2),n >= 2; 因此,我们能很快根据表达式写出递归版的代码: 1/*fibo.c*/ 2#include <stdio.h> 3#include <stdlib.h&

求斐波那契数列第n位的几种实现方式及性能对比(c#语言)

在每一种编程语言里,斐波那契数列的计算方式都是一个经典的话题.它可能有很多种计算方式,例如:递归.迭代.数学公式.哪种算法最容易理解,哪种算法是性能最好的呢? 这里给大家分享一下我对它的研究和总结:下面是几种常见的代码实现方式,以及各自的优缺点.性能对比. Iteration using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; public class Progr

以计算斐波那契数列为例说说动态规划算法(Dynamic Programming Algorithm Overlapping subproblems Optimal substructure Memoization Tabulation)

动态规划(Dynamic Programming)是求解决策过程(decision process)最优化的数学方法.它的名字和动态没有关系,是Richard Bellman为了唬人而取的. 动态规划主要用于解决包含重叠子问题的最优化问题,其基本策略是将原问题分解为相似的子问题,通过求解并保存重复子问题的解,然后逐步合并成为原问题的解.动态规划的关键是用记忆法储存重复问题的答案,避免重复求解,以空间换取时间. 用动态规划解决的经典问题有:最短路径(shortest path),0-1背包问题(K

EOJ 3506. 斐波那契数列

题意:给一个斐波那契数,问是斐波那契数列中的第几个,范围比较大是1到第1e5个斐波那契数 题解:选几个大质数MOD一下,预处理出范围内的所有膜后的值,如果输入的数在取模后能够和某一项斐波那契数的膜一一对应,那么他很大概率的就是它(类似hash???) p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #c81b13 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 1

用递归和非递归的方法输出斐波那契数列的第n个元素(C语言实现)

费波那契数列(意大利语:Successione di Fibonacci),又译为费波拿契数.斐波那契数列.费氏数列.黄金分割数列. 在数学上,费波那契数列是以递归的方法来定义: {\displaystyle F_{0}=0} {\displaystyle F_{1}=1} {\displaystyle F_{n}=F_{n-1}+F_{n-2}}(n≧2) 用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就是由之前的两数相加而得出.首几个费波那契系数是: 0, 1, 1, 2, 3

Fibonacci斐波拉契数列----------动态规划DP

n==10 20 30 40 50 46 体验一下,感受一下,运行时间 #include <stdio.h>int fib(int n){ if (n<=1)     return 1; else            return fib(n-1)+fib(n-2); }int main( ){ int n; scanf("%d",&n); printf("%d\n" ,fib(n) );} 先 n==10 20 30 40 50 46

《剑指Offer》题目——斐波拉契数列

题目描述:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.(n<=39) 题目分析:如果使用简单的递归,很容易造成栈溢出.采用递推的方式即可. 代码: public class Fibonacci { public static int fibonacci(int n){ int res[] = new int[2]; res[0]=1; res[1]=1; int temp = 0; if(n==0) return 0; if(n<=2) return res[