面试题:求最长公共子串(利…

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:  

  设两个字符串u和v,若u和v的长度>=k,则  u<kv 当且仅当 uk<vk  

                                               u=kv 当且仅当 uk=vk  

                      若u或v的长度<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) 且  

       Suffix(i+k)<kSuffix(j+k))。

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-前缀关系下从小到大的“名次”.

     (通过SA_k很容易在O(n)的时间内求出Rank_k。)

2)若已经生成了SA_2^k,2^k>n,则生成了SA。

     否则,在O(n)时间内可以生成Rank_2^k,则很容易生成 SA_2^(k+1),由于Suffix(i)的长度为n-i+1,

在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]。

这样我们就获得了一天中任意时间的在线人数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值