算法导论——字符串搜索Rabin-Karp算法实现

算法导论——字符串搜索Rabin-Karp算法实现

Rabin-Karp算法是基于指纹的字符串匹配算法,这里就不得不提到一些听歌识曲的算法——Shazam,也是通过音乐指纹来进行乐曲的匹配。思路很简单,不过坑却很多。

理论

R a b i n − K a r p Rabin-Karp RabinKarp算法的理论很简单,要点有二:

  • 其一,Hash函数求子字符串的指纹;
  • 其二,已知前一个子字符串的指纹求下一个子字符串的指纹;

其中, H a s h Hash Hash函数的定义通常如下:
H a s h ( ′ a 0 a 1 . . . a n ′ ) = a n ∗ b a s e 0 + a n − 1 ∗ b a s e 1 + . . . + a 0 ∗ b a s e n ( 1 ) Hash('a_0a_1...a_n')=a_n*base^0+a_{n-1}*base^1+...+a_0*base^n (1) Hash(a0a1...an)=anbase0+an1base1+...+a0basen(1)
b a s e base base是基数,通常取字符集的个数,如字符集合 { a , b , c , d . . . x , y , z } \{ a,b,c,d...x,y,z\} {a,b,c,d...x,y,z}就是小写英文字母集合,共26个。我们的字符串的元素应该全部取自这个集合,那么有 b a s e = 26 base=26 base=26
至于已知前一个子字符串的指纹求下一个子字符串的指纹的过程可由一张图概括:
在这里插入图片描述


f t i + 1 = b a s e ∗ ( f t i − T s ∗ 1 0 m − 1 ) + T s + m ( 2 ) ft_{i+1}=base*(ft_i-T_s*10^{m-1})+T_{s+m} (2) fti+1=base(ftiTs10m1)+Ts+m(2)

Rabin-Karp算法求模改进

如果说 ′ a 0 a 1 . . . a n ′ 'a_0a_1...a_n' a0a1...an过长,那么计算机就难以求解 H a s h ( ′ a 0 a 1 . . . a n ′ ) Hash('a_0a_1...a_n') Hash(a0a1...an),或者说结果很大可能会溢出,为了解决这个问题,我们使用求余。改变(1)式和(2)式,得到:
在这里插入图片描述
在这里插入图片描述
具体证明可以看我上几次的博客
算法导论——Rabin-Karp算法求模证明(1)
算法导论——Rabin-Karp算法求模证明(2)

不多BB了,开始实践

C#实践

Program.cs

using System;
using System.Collections.Generic;

namespace StringMatch
{
    class Program
    {
        static void Main(string[] args)
        {
            
            string T = "Star, I Want to Love with U, I'm so in Love with U";
            string P = "Love with U";

            //Base表明字符集一共有多少个元素,q为大于T[i]值的元素
            Console.WriteLine("Rabin-Karp算法");
            RabinKarp solver = new RabinKarp();
            //ASCII码共128个码元,设置Base为128,q为大于T[i]的数,这里选择129即可。另外,算法导论上面要求q为质数,这样可以进一步减少时间复杂度,具体为何,博主由于知识有限,并没有做详细论证。
            List<int> result = solver.RabinKarpStategy(T, P, 128, 129);
            GetResult(result, P, T);
        }

    }
}

RabinKarp.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StringMatch
{
    public class RabinKarp
    {
        public List<int> RabinKarpStategy(string T, string P, int Base, int q)
        {
            List<int> result = new List<int>();
            int n = T.Length;
            int m = P.Length;
            double ft = Hash(T.Substring(0, m), Base, q);
            double fp = Hash(P, Base, q);
            double c = 1;
            //double c = Math.Pow(Base, m - 1) % q;,直接算易越界,结果会出错,我们用下面的算法
            //此时字符串相当于"10000...00",注意我的意思不是ASCII码的1、0,而是真正的1、0,大家带入公式即可得到下面的结果
            for(int i = 0; i < m - 1; i++)
            {
                c = (c * Base) % q;
            }
			
			//演算结果
            for (int s = 0; s <= n - m; s++)
            {
                if (IsEqualByMod(ft,fp,q))
                {
                	//Hash相同,不一定匹配,进一步考虑,如果选择q为质数,或许可以减少Hash相同的情况
                    if (T.Substring(s, m) == P)
                    {
                        result.Add(s);
                    }
                }

                if (s < n - m)
                {
                	//可能为负数,我们需要充分考虑
                    ft = (((ft - (T[s] * c)) * Base) + T[s + m] ) % q;                    
                }
            }
            return result;

        }
		
		//防止负数情况
        private bool IsEqualByMod(double ft, double fp,int q)
        {
            if (ft < 0)
            {
                if (ft + q == fp)
                {
                    return true;
                }
            }
            else
            {
                if (fp + q == ft)
                {
                    return true;
                }
            }
            return false;
        }

        private double Hash(string p, int Base, int q)
        {
            double hashValue = 0;
            for(int i = 0; i < p.Length; i++)
            {
                hashValue = (hashValue * Base + p[i]) % q;
            }
            return hashValue;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值