20171007
【字符串算法】
- 模式匹配----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值: 31、131、1313、13131、131313..
例如我们取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. 口吃的外星人
题意:有一个外星人说的话里包含很多重复的字符串,如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排序
顺序访问子树,转化为子问题,此题得解。