国庆七天乐——第七天

20171007

【字符串算法】

  1. 模式匹配----kmp

定义:next[i+1]是最大的j+1使得p[0~j]是p[0~i]的后缀

通过这个next数组来跳过某些冗余计算

                       

作用:当模式串p的长度为j的前缀是长度为i前缀的后缀时,若文本串在i+1的位置失配,则指针可跳到j继续尝试与j+1位置匹配

如何求next指针

假设已求得前i个next,要求next[i+1]

设next[i]=j

若p[j]!=p[i]则使j=next[j]

若p[i]==p[j](或j出界)则next[i+1]=j+1(或0)

【复杂度】

可以证明或者从代码中简单地看出,kmp算法复杂度是O(n+m)

简单匹配当不match的时候要回溯

Kmp算法只需要每次不匹配的时候跳一次next即可

复杂度降到了线性。

证明:

KMP 算法的时间复杂度由预处理和匹配两部分组成

外层的 i 循环和内层的 j 循环是完全独立的,时间复杂度可以单独计算

i循环的时间复杂度明显是 O(N)

j本身有加有减,但易知其下降的总次数不会超过增加的总次数( j 每次固定+1,但每次至少-1)

j 每次增加都随i加1,故增加的总量为N,j下降的次数不会超过N

故 j 循环的时间复杂度也是 O(N)

总过程的时间复杂度为 O(N) 总时间复杂度是 O(N + M)

*********************例题*****************************

1.Poj2406

题意:给一个字符串,问字符串中有多少个循环

根据kmp中next数组的性质来求解

Next数组性质: p[0~j]是p[0~i]的后缀,A=B

显然x=z,y=w

又因为y=z

所以x=y=z=w

……

由数学归纳法,得x为原串的最小循环节

2.NOI2014

题意:给定一个长为L的字符串(L<=100W),求一个num数组,num[i]表示长度为i的前缀中字符串S’的数量,其中S‘既是该前缀的前缀也是该前缀的后缀,且|S'|*2<=i

Kmp变式

定义由i=next[i]转移到-1的步数是dep[i]

从i跳到j<=i/2的位置,num[i]=dep[j] 想想为什么?

然后就两次kmp就好了

【字符串哈希】

字符串具有离散性强,长度大等特点,不易比较

如果给每个字符串一个数值做标签,比较的时候直接比较这个标签就能确定字符串是否匹配。

这个标签就是字符串HASH函数

常用的HASH函数(BKDR HASH):

也有其他花式HASH函数,在此不一一例举

参考资料: http://blog.csdn.net/djinglan/article/details/8812934

代码:

常用的seed值: 31131131313131131313..  

例如我们取x=131

求串s=pekinguniversity hash[s]

Hash[s]=p*131^15+e*131^14+…+y*131^0

 

那么如果要在文本中找到串s,只用维护一个长度为16的子串的hash值并与hash(s)比较即可

维护长度为l的子串的hash

设已知hash[s[ i-l+1 ~ i ]]=h,要求h1=hash[s[ i-l ~ i+1 ]]

h1=(h-s[i-l+1]*seed^(l-1))*seed+s[i+1]*seed^0               

******************例题*******************************

  1. 1.     口吃的外星人

题意:有一个外星人说的话里包含很多重复的字符串,如ababa包含两个aba,给定这个外星人说的一句话,找出至少出现m词的最长字符串,字符串长度4e4

解法:二分答案L,判断长度L的子串最多出现多少次

维护长度L的子串hash,若某hash值出现m次则有解

复杂度O(n log n)

【回文串manacher】

对称轴可能是字符串中的一个位置或者一个间隙

避免重复操作?

把间隙变成字符!

aba —> #a#b#a#

abba —> #a#b#b#a#

Def: r[i]->以i为对称轴的最长匹配半径

e.g.

      0 1 2 3 4 5 6

      # a # b # a #

r:    0 1 0 3 0 1 0

r恰好是最长回文串的长度

(因为加了#的缘故,所以字符串长度变成了两倍,而r是枚举最长子串的半径,所以r最长就是回文子串最长)

先定义两个指针

Pos:一个回文串的对称轴

Max_r:max(pos+r[pos])                                              

情况1:i<max_r,取j为i关于pos的对称位置

当r[j]+i>max_r时,i~max_r的一段必然可以匹配成回文串

而max_r+1的位置开始需要逐位匹配

Max_r=i+r[i],pos=I

情况2:i<max_r,取j为i关于pos的对称位置

r[j]+i<=max_r

r[i]=r[j]

情况3:i>=max_r

从i开始往右逐位匹配,max_r=r[i]+i

***********************例题*************************

题目大意:

就是现在给出一个长度为 n 的字符串(1 <= n <= 10^6)和一个正整数K(1 <= K <= 10^12)

对于给出的长度为n的字符串如果其回文串的数量比K少则输出-1, 否则输出所有回文串中长度为奇数的最长的前K个回文串的长度的乘积, 结果对于19930726取模输出

解法:

首先可以用manacher算法确定每个位置的回文半径, 由于这里只需要长度是奇数, 所以不需要再原来的字符串的相邻两个字符之间插入未出现的字符, 直接在首尾添加好不同字符之后盘一遍manacher算法, 对于位置i为中心的回文半径R[i], 用dp[i]来表示相邻长度的回文串的数量差分(其实就是一个常用的前缀和技巧, 因为这里每次更新[1, R[i]]这个区间 + 1, 而只在所有更新完毕之后才查询所以没有必要使用树状数组, 直接根据每次更新的时候dp[1]++, dp[R[i] + 1]--, 最后后dp[1~i]的和就是最终ans[i]的值, 即长度为2*i - 1的回文串的数量,然后用快速幂就可以了

【考试2】

T1字符串

Manacher板子

T2动态规划

状态压缩dp,估计深搜广搜优化一下也能过

F(I,j,p)表示第i时间段选择课程为j,此时combo为p的最大收获量

Add是j状态i时刻能获得的收获

当状态与选课吻合时

F(I,j,p)=max{f(I-1,k,p-1)}+add (1<=p<=10)

当选课与状态不吻合时

f(I,j,0)=max{f(i-1,k,p)+p*(p-1)/2}+add (0<=p<=10)

T3图论

最小生成树加dfs加贪心

因为边是以亿为单位的1e8数量级,而且不同边价格不同,所以n-1条边结果一定最优,所以买边和访问是分开的两个问题。

先最小生成树建边,把花费*1e8加入答案。

Dfs计算访问一颗子树的时间T[i]和每颗子树的费用和sum[i]

贪心,策略类似于国王游戏

由于限制访问次数,访问子树时必定先遍历完一颗子树再去访问另一颗

那么当存在两颗子树时,设T0,sum0,T1,sum1

若先访问0,则1的等待时间和等待花销已知,为t0*sum1

同理若先访问1,则0的花销为t1*sum0

当t0/sum0<t1/sum1时,先访问t0更优

对每个点的子树的ti/sumi排序

顺序访问子树,转化为子问题,此题得解。

原文地址:https://www.cnblogs.com/ZDHYXZ/p/7635550.html