1、给定一个字符串,求出其最长的重复子串。
如字符串abcdabcabcd,求的最长重复子串为abcd
解析:
step1、生成一个数组,数组的元素是后缀字符串,
A[]={ Suffix(1), Suffix(2),..., Suffix(n)} , 其中 Suffix(i) ==r[i..len(r)] ,即后缀指的是字符串S从某个位置 i 开始到整个串末尾结束的一个特殊子串。
时间复杂度是O(n)。
Step2、对这个数组进行排序,生成一维后缀数组SA,它保存 1..n 的某个排列 SA[1 ] ,SA[2] , …… ,SA[n] ,并且保证 Suffix(SA[i]) < Suffix(SA[i+1]) ,1 ≤ i<n 。也就是将 S 的 n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入 SA 中。
后缀数组生成的思路:
方法一:最直接最简单的方法当然是把S的后缀都看作一些普通的字符串,按照一般字符串排序的方法对它们从小到大进行排序,即对A进行排序。时间复杂度是O(n^2)。
方法二:倍增算法(Doubling Algorithm),它正是充分利用了各个后缀之间的联系,将构造后缀数组的最坏时间复杂度成功降至O(nlogn)。
1)Def1:对一个字符串u,我们定义u的k-前缀:
定义k-前缀比较关系<k、=k和≤k:
(直观地看这些加了一个下标k的比较符号的意义就是对两个字符串的前k个字符进行字典序比较。)
根据前缀比较符的性质我们可以得到以下的非常重要的性质:
性质1.1 对k≥n,Suffix(i)<kSuffix(j) 等价于 Suffix(i)<Suffix(j)。
性质1.2 Suffix(i)=2kSuffix(j)等价于 Suffix(i)=kSuffix(j) 且 Suffix(i+k)=kSuffix(j+k)。 性质1.3 Suffix(i)<2kSuffix(j) 等价于 Suffix(i)<kS(j) 或 (Suffix(i)=kSuffix(j) 且
Def2: k-后缀数组SA_k保存1..n的某个排列SA_k[1],SA_k[2],…SA_k[n]使得Suffix(SA_k) ≤kSuffix(SA_k[i+1]),1≤i<n。也就是说对所有的后缀在k-前缀比较关系下从小到大排序,并且把排序后的后缀的开头位置顺次放入数组SA_k中。
Def3:k-名次数组Rank_k,Rank_k代表Suffix(i)在k-前缀关系下从小到大的“名次”.
2)若已经生成了SA_2^k,2^k>n,则生成了SA。
在2^(k+1)-前缀意义下,
Suffix(i)=Suffix(j)等价于:
n-i+1<=2^k且Rank_2^k[i]=Rank_2^k[j]。
或
n-i+1>2^k且Rank_2^k[i]=Rank_2^k[j]且Rank_2^k[i+2^k]=Rank_2^k[j+2^k]。
Suffix(i)<Suffix(j)等价于:
n-i+1<=2^k且Rank_2^k[i]<Rank_2^k[j]。
或
n-i+1<=2^k且Rank_2^k[i]=Rank_2^k[j]且Rank_2^k[i+2^k]<Rank_2^k[j+2^k]。
可以在O(nlogn)的时间内求出SA_2^0和Rank_2^0。
便可在O(n)的时间内成生成SA_2^1和Rank_2^1。
...循环k次。2^k+1=n.
因此计算出后缀数组SA和名次数组Rank总时间复杂度是O(nlogn).
Step2、根据后缀数组SA,找出SA中任意两个相邻元素对应的字符串的开头公共部分,这样便可得到n-1个公共子串,即n-1个重复子串。再找出它们之中最大的那个字串即是最大的重复子串。时间复杂度是O(n*n).
因此整个程序的时间复杂度是O(n)+O(nlogn)+O(n*n)=O(n*n)。
2、腾讯笔试题:统计论坛在线人数分布
求一个论坛的在线人数,假设有一个论坛,其注册ID有两亿个,每个ID从登陆到退出会向一个日志文件中记下登陆时间和退出时间,要求写一个算法统计一天中论坛的用户在线分布,取样粒度为秒。
解析:一天共有24*3600=86400秒。定义数组int delta[86400],数组的每个元素初始化为0,用来存放在一秒时间内在线人数的改变量,,在第i秒,若有m个ID上线,n个下线,则delta[i]=m-n,定义数组int online[86400],用来存放每一秒的在线人数,初始化online[0]=0,则online[i]=online[i-1]+delta[i],如此便统计出了每一秒论坛的在线人数。
解析:一天总共有 3600*24 = 86400秒。
定义一个长度为86400的整数数组int delta[86400],每个整数对应这一秒的人数变化值,可能为正也可能为负。开始时将数组元素都初始化为0。
然后依次读入每个用户的登录时间和退出时间,将与登录时间对应的整数值加1,将与退出时间对应的整数值减1。
这样处理一遍后数组中存储了每秒中的人数变化情况。
定义另外一个长度为86400的整数数组int online_num[86400],每个整数对应这一秒的论坛在线人数。
假设一天开始时论坛在线人数为0,则第1秒的人数online_num[0] = delta[0]。第n+1秒的人数online_num[n] = online_num[n-1] + delta[n]。
这样我们就获得了一天中任意时间的在线人数。