庞果英雄会-合法字符串 复杂度 O(log(n)*log(n)*log(m))

合法字符串

      --解题报告

题目描述:

http://hero.pongo.cn/Question/Details?ID=74&ExamID=72

合法字符串

·        有 效 期:

·        庞果网

·        2013-07-242014-01-25

·        难 度 等 级:

·        答 题 时 长:

·        编程语言要求:

·        

·        180分钟

·        C++Java C#

题目详情

用n个不同的字符(编号1 - n),组成一个字符串,有如下2点要求:

    1、对于编号为i的字符,如果2 * i > n,则该字符可以作为最后一个字符,但如果该字符不是作为最后一个字符的话,则该字符后面可以接任意字符;
    2、对于编号为i的字符,如果2 *i <= n,则该字符不可以作为最后一个字符,且该字符后面所紧接着的下一个字符的编号一定要 >=2 * i。

问有多少长度为M且符合条件的字符串。

例如:N = 2,M = 3。则abb,bab, bbb是符合条件的字符串,剩下的均为不符合条件的字符串。

 

输入:n,m  (2<=n,m<=1000000000)

输出:满足条件的字符串的个数,由于数据很大,输出该数Mod 10^9 + 7的结果。

 

函数头部

int validstring(int n,int m) {

}

答题说明

1、main函数可不用完成

2、对于英雄会或本题有任何反馈或意见,欢迎加入英雄会编程挑战交流QQ群:216133772,验证信息为你的CSDN昵称。

 

思路分析:

题目数据规模非常大,出看貌似传统的优化算法都不能用,做一次运算复杂度就是10^9 超时。然而,这只是表面的。这道题真正的难点在于模型构造发现规律

模型1:动态规划模型

dp[j][i]:表示以字符i (0<i<=n)结尾,长度为j(0<j<=m) 的字符串string的个数。其中,string可能不是合法的,但是若在string后面加上一个题目中的[1类]字符那么string将是合法的。

由此就有递推方程:

dp[j][i] =

①   dp[j-1][1]+…+dp[j-1][n] (若i+i>n,即i是[1类]字符)

②   dp[j-1][1]+…+dp[j-1][t] ,t+t<=I  (若i+i<=n,即i是[2类]字符)

初始状态dp[1][i]=1 (0<i<=n) 长度为1以字符i结尾当然只有一个

最终结果,dp[m][n-n/2]+…dp[m][n],真正的合法字符一定以[1类]字符结尾.

 

复杂度O(m*n*n) 【空间=m*n, 转移=n】

动态规划不能解决这个问题,但是是必经之路

 

模型2:数列递推模型

描述:通过把数列递推公式转换为矩阵来计算数列的第n项

例   f(n)=2*f(n-1)+f(n-2),f(1)=f(0)=1

则递推公式可以转化为递推矩阵:

       [f(n-2),f(n-1)] * A = [f(n-1), f(n)],A=[0 1;1 2]

即[f(n-1), f(n)] = [f(0), f(1)]*A^(n-1)

数列递推模型把求解数列的第n项转化为求解矩阵的n次幂

结合模型1和模型2 ,可以把动态规划的递推方程转换成矩阵的形式:

       [dp(m-1,1),… , dp(m-1,n-1) dp(m-1,n)] * A = [dp(m,1), …, dp(m,n-1), dp(m,n)]

A为n*n的矩阵,当n=6时,A=

0 11 1 1 1

0 00 1 1 1

0 00 0 0 1

1 11 1 1 1

1 11 1 1 1

1 11 1 1 1

模型3:快速幂模型

描述,用快速幂模型可以把求解n次幂的问题转化为求解log(n)次幂的问题

例:若要求解a^9

s= a^9 = a*(a^8) = a* (a^2)^4  //把规模为9的问题,通过一次操作转化为规模为4的问题

即,若n=2*k,则s= a^n = (a^2)^k  //先把a自乘,然后求k次幂

       若n=2*k+1,则s= a^n =a *a^(2*k)  //先拿出一个a,转化到n为偶数的情况

 

伪代码如下

int fastmult(a ,n){

int ans =1;

       while(n>0){

              if(n is odd) ans=a*ans;

              a=a*a;

              n>>=1;

       }

return ans;

}

快速幂的指数每次都会减半,因此算法复杂度由n缩减到log(n)

 

快速幂模型,求解矩阵的n次幂,同样有效。

模型4:同余定理

       (A+B)% M    =    (A%M+ B%M ) % M

       (A*B)% M    =    (A%M* B%M ) % M

       (A- B) % M   =     (A%M - B%M) % M

同余的核心要素:取模不分先后

根据同余定理,结合题目的Mod 10^9 + 7,可以在计算过程中先取模,再进行加减乘运算。

 

规律:

接着模型2说,(其余的模型在解题过程中需领悟)

[dp(m-1,1), … , dp(m-1,n-1) dp(m-1,n)] * A =[dp(m,1), …, dp(m,n-1), dp(m,n)]

初始状态dp[1][i]=1 (0<i<=n) 长度为1以字符i结尾只有一个

最终结果,dp[m][n-n/2]+…dp[m][n],真正的合法字符一定以[1类]字符结尾.

A为n*n的矩阵,当n=6时,A=

0 11 1 1 1

0 00 1 1 1

0 00 0 0 1

1 11 1 1 1

1 11 1 1 1

1 11 1 1 1

对于任意的n,矩阵A仅由0和1构成,且满足一个规律:

第1行1个0,

第2行3个0,

第3行5个0,

第r行2*r+1个0,      0< r <= n/2

。。。等差数列

第r行没有0,                n/2 < r<=n

 

下面以n=6继续发现规律:

dp[1]=[1 1 1 1 1 1]

S=[0 0 0 1 1 1]T (转置)

T= dp[m] *S, (T就是最终的求解目标)

T= dp[1]*A^(m-1)*S

其中dp[1]是矩阵A的第n行,S是矩阵A的第1列

因此T=AA(n,1),(第n行第1列),其中 AA=A^(m+1)【重要规律】

以下重点目标是发现矩阵A^m的规律,重点关注第一列

令   f(m)表示矩阵A^m的第一列,

       f(m,i)表示矩阵A^m的第i行的第一个元素 【f(m+1,n)为目标】

[1].  f(m,i)=f(m-1,2*i) +f(m-1,2*i+1)+…+f(m-1,n)     (0< i <= n/2)

[2].  f(m,i)=f(m,n)      (n/2 < i <=n)

[3].  f(m,n)=f(m,1)+f(m-1,1)

根据以上三个递推公式,尝试构造f(m,n)的递推公式,即构造

f(m,n) = f(m-1,n)*k1 + f(m-2,n)*k2+ … + f(m-r,n)*kr

 

其中递推长度是r,且规模上r=log(n)

如果找到了这r个系数,那么求解f(m,n)就可以转换到数列递推模型快速幂模型,从而解决该问题。求解这r个系数是此问题的核心问题。

 

核心:r个系数的求解

根据公式[1].和公式[2]. 合并相同的项

a)       f(m,i)=f(m-1,2*i) +f(m-1,2*i+1)+…+f(m-1,n/2)+(n-n/2)*f(m-1,n)        (0< i <= n/4)

b)       f(m,i)= (n-2*i)*f(m-1,n)   (n/4< i<= n/2)

       [边界为概数]

对于a)类情况,f(m,i)对递推项f(m-1,n)的系数有贡献,对f(m-r,n)也有贡献(r>1)

对于b)类情况,f(m,i)只对递推项f(m-1,n)的系数有贡献

 

引入贡献向量,和贡献递推矩阵,以n=22为例:

m-4

m-3

m-2

m-1

i=1

20

114

80

11

i=2

0

20

58

11

i=3

0

0

36

11

i=4

0

0

16

11

i=5

0

0

4

11

i=6

0

0

0

11

i=7

0

0

0

9

i=8

0

0

0

7

i=9

0

0

0

5

i=10

0

0

0

3

i=11

0

0

0

1

 

f(m-4,n)

f(m-3,n)

f(m-2,n)

f(m-1,n)

贡献矩阵意义

第一行[20 114 80 11] 表示:

f(m,1)= f(m-1,n)*11+ f(m-2,n)*80+ f(m-3,n)*114+f(m-4,n)*20

第二行[20 58 11] 表示:

f(m,2)= f(m-1,n)*11+ f(m-2,n)*58+ f(m-3,n)*20

以此类推。。。

贡献矩阵的求法:

其中贡献矩阵的第m-1列由一个等差数列和一个常数数列构成,数值上等于f(2)的前半部分

其余列g(i,j)=g(2*i,j+1)+g(2*i+1,j+1)+…+ g(n/2,j+1) (j<m-1)

何时停止呢,一直到g(1,j)=0停止

这里可以通过数列递推模型把递推公式转换为矩阵,但是转换之后的矩阵规模依然难以接受,所以求解贡献矩阵还需要在思考。

根据公式[2]f(m,n)=f(m,1)+f(m-1,1)我们只关心贡献矩阵的第一行。

因此,需要消耗点脑汁,做一点转换:

把贡献矩阵的第m-1列,转换为两个等差数列相减,

如[1 3 5 5 5] = [1 3 5 7 9] - [0 0 0 2 4]

然后对两个等差数列单独求贡献矩阵,把求解结果的第一行做差就是我们需要的结果。

 

最后一步

贡献矩阵g的最后一列是等差数列,如何求解g的第一行?【数列递推模型】

a(n)表示数列的通项,s(n)是前n项和。

 [1,a(n-1), s(n-2)] * C = [1, a(n), s(n-1)]            递推式(1)

当数列a(n)是等差数列时,若 a(n)=a+(n-1)*d

C=[1 0 0;d 1 0;0 1 1]

则递推式(1)就是等差数列的表达,[1,a,0]*C^(n/2)则可求解第m-1列的第一行的元素。

有了s(n) 和a(n) ,求解第m-2列会方便很多,同样是构造递推矩阵。

即,根据求解第m-1列递推矩阵构造求解第m-2的递推矩阵。D=C*C U [0,…., 1 , 1]

以此类推,经过r次构造就可以求解贡献矩阵。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值