算法练习三

算法分析与设计 练习三

内容

分别用KMP、Monte Carlo和Las Vegas算法编写subString,随机生成10000对、长度较长、且长度不等的01文本串X(haystack)和模式Y(needle)(三个程序处理相同的串)

假设文本串长度为size1,模式串长度为size2

1.subStringKMP

思路:

(1).KMP的主要思想
当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。

(2).next数组 == 前缀表(prefix table)
1)前缀表记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
2)前缀表是用来回退的,它记录了模式串与文本串不匹配的时候,模式串应该从哪里开始重新匹配。
KMP

3)下标5(包括5)之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa 。因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了。

(3).构造next数组
其实就是计算模式串s,前缀表的过程。
1)初始化
2)处理前后缀不相同的情况:向前回溯
3)处理前后缀相同的情况

2.subStringMC

Monte Carlo算法:

总能得到问题的一个解,但不一定是正确解。然而可以通过多次运行原算法,并且满足每次运行时的随机选择都相互独立,这样就使产生非正确解的概率减到任意小。

思路:

通过对字符串“取指纹”判断字符串是否相等
取指纹的方法:

由于文本串和模式串是由0-1组成,将文本串和模式串由二进制转换为十进制。选取一个质数P ,对文本串和模式串的十进制编码与P做取模运算。若取模结果相同,则视为二者匹配。

文本串取指纹的优化方法:

由于在文本串中相邻的子串的“取指纹”值满足下面的关系:

getFp(X(i + 1)) = (2 * getFp(X(i)) - 2 ^ size2 * X[i] + X[i + size2]) % P,

其中getFp为取指纹函数,X(i)表示文本串中以X[i]为开头,长度为size2的子串

3.subStringLS

Las Vegas算法:

不断调用随机算法求解,直到求得正确解或调用次数达到某个阈值。所以,不一定能得到解,如果能得到解,一定是正确解。

思路:

在Monte Carlo算法的基础上进行优化:

当两字符串的取指纹结果相等时,则进行逐一比对,若比对结果相同,则二者匹配;若比对结果不同,则继续向后进行模式匹配。

4.源码
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <ctime>
#include <cmath>

using namespace std;

//构造next数组
void getNext(vector<int>& next, const string& needle)
{
    //初始化
    int j = -1;
    next[0] = j;

    for(int i = 1;i < needle.size();++i)
    {
        //前后缀不相同的情况:向前回溯
        while (j >= 0 && needle[i] != needle[j + 1])
        {
            j = next[j];
        }

        //前后缀相同的情况
        if(needle[i] == needle[j + 1])
        {
            ++j;
        }

        next[i] = j;
    }
}

//KMP算法,从文本串haystack中找到模式串needle第一次出现的位置
int subStringKMP(string haystack, string needle)
{
    if (needle.size() == 0) return 0;
    if (haystack.size() == 0) return -1;
    if (haystack.size() < needle.size()) return -1;

    int size1 = haystack.size();
    int size2 = needle.size();

    vector<int> next(size2);
    getNext(next, needle);

    int j = -1;
    for(int i = 0;i < size1;++i)
    {
        while(j >= 0 && haystack[i] != needle[j + 1])
        {
            j=next[j];
        }

        if(haystack[i] == needle[j + 1])
        {
            ++j;
        }

        if (j == size2 - 1)
        {
            return i - size2 + 1;
        }
    }

    return -1;
}

//大素数
const int mod = 1e9 + 7;

//辅助函数, 将二进制转换为十进制
long long binaryToDecimal(string s)
{
    int size = s.size();

    long long sum = 0;

    for (int i = 0;i < size;++i)
    {
        if (s[i] == '1')
        {
            sum = sum + pow(2, size - 1 - i);
        }
    }

    return sum;
}

//辅助函数,取指纹
int getFingerprint(long long decimal)
{
    return decimal % mod;
}


//Monte Carlo算法,从文本串haystack中找到模式串needle第一次出现的位置
int subStringMC(string haystack, string needle)
{
    int size1 = haystack.size();
    int size2 = needle.size();
    if (size2 == 0) return 0;

    long long needleDecimal = binaryToDecimal(needle);
    long long haystackDecimal = binaryToDecimal(haystack.substr(0, size2));

    int needleFingerprint = getFingerprint(needleDecimal);
    int haystackFingerprint = getFingerprint(haystackDecimal);

    for (int i = 0;i <= size1 - size2;++i)
    {
        if (needleFingerprint == haystackFingerprint)
        {
            return i;
        }

        haystackFingerprint = 
        (2 * haystackFingerprint - static_cast<int>(pow(2, size2))* haystack[i]          + haystack[i + size2]) % mod;
    }

    return -1;
}

//Las Vegas算法,从文本串haystack中找到模式串needle第一次出现的位置
int subStringLV(string haystack, string needle)
{
    int size1 = haystack.size();
    int size2 = needle.size();
    if (size2 == 0) return 0;

    long long needleDecimal = binaryToDecimal(needle);
    long long haystackDecimal = binaryToDecimal(haystack.substr(0, size2));

    int needleFingerprint = getFingerprint(needleDecimal);
    int haystackFingerprint = getFingerprint(haystackDecimal);

    for (int i = 0;i <= size1 - size2;++i)
    {
        if (needleFingerprint == haystackFingerprint)
        {
            int j;
            for (j = 0; haystack[i + j] == needle[j] && j < size2; ++j);
            if (j == size2) return i;
        }

        haystackFingerprint = 
        (2 * haystackFingerprint - static_cast<int>(pow(2, size2))* haystack[i]          + haystack[i + size2]) % mod;
    }

    return -1;
}

int main()
{
    //文本串数组
    vector<string> haystacks(10000);
    //模式串数组
    vector<string> needles(10000);

    //初始化
    srand(time(0));
    for (int i = 0;i < 10000;++i)
    {
        for (int j = 0;j < 500;++j)
        {
            haystacks[i].push_back((rand() % 2) + '0');

        }

        for (int k = 0;k < 50;++k)
        {
            needles[i].push_back((rand() % 2) + '0');

        }
    }

    //KMP算法
    vector<int> result1(10000); //存储10000对字符串的匹配结果
    clock_t start1 = clock();
    for (int i = 0;i < 10000;++i)
    {
        result1.emplace_back(subStringKMP(haystacks[i], needles[i]));

    }
    clock_t finish1 = clock();

    double duration1 = static_cast<double>(finish1 - start1);
    cout << "KMP算法的运行时间为: " << duration1 << "毫秒" << endl;

    //Monte Carlo算法
    vector<int> result2(10000); //存储10000对字符串的匹配结果
    clock_t start2 = clock();
    for (int i = 0;i < 10000;++i)
    {
        result2.emplace_back(subStringMC(haystacks[i], needles[i]));

    }
    clock_t finish2 = clock();

    double duration2 = static_cast<double>(finish2 - start2);
    cout << "Monte Carlo算法的运行时间为: " << duration2 << "毫秒" << endl;

    //Las Vegas算法
    vector<int> result3(10000); //存储10000对字符串的匹配结果
    clock_t start3 = clock();
    for (int i = 0;i < 10000;++i)
    {
        result3.emplace_back(subStringLV(haystacks[i], needles[i]));

    }
    clock_t finish3 = clock();

    double duration3 = static_cast<double>(finish3 - start3);
    cout << "Las Vegas算法的运行时间为: " << duration3 << "毫秒" << endl;

    int count = 0;
    for (int i = 0;i < 10000;++i)
    {
        if (result1[i] == result2[i])
        {
            ++count;
        }
    }
    double accuracy = count / 10000;
    cout << "Monte Carlo算法的准确率为:" << accuracy * 100 << "%" << endl;

    bool flag = (result1 == result3);
    if (flag)
    {
        cout << "Las Vegas算法正确" << endl;

    }

    return 0;
}
6.时间复杂度

KMP: O(size1 + size2)

Monte Carlo: O(size1 + size2)

Las Vegas: O(size1 + size2)

7.结论

随机数P应当取一个较大的质数。当P一定大时,Monte Carlo算法的出错率要比1/size1小得多 。而Las Vegas算法一定正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值