字符串相关模板

1.字符串匹配

寻找字符串S中字符串T出现的位置或次数的问题属于字符串匹配问题

1.哈希算法

S的长度为n,T的长度为m。朴素方法,很容易想到,时间复杂度为O(nm).
下面介绍哈希算法
对于每个起始位置,我们不是O(m)直接比较字符串是否匹配,而是O(l)的比较长度为m的字符串的哈希值与T的哈希值是否相等。虽然哈希值相等字符串未必相等,但是若哈希值随机分布,不同字符串哈希值相等的概率很低,可以当做几乎不会发生。
如果采用O(m)的算法计算长度为m的字符串子串的哈希值,复杂度仍为O(nm)。这里采用滚动哈希的优化技巧。选取两个合适的互素的常数h和b(l<b<h),假设字符串C=c1c2c3……cm,定义哈希函数H(C)=(c1*b^(m-1)+c2*b^(m-2)+……cm*b^0)modh。其中b是奇数,相当于把字符串看成k进制数。利用S[k,k+m-1]的哈希值,就可以计算S[k+1,k+m]的哈希值.H(S[k+1,k+m])=(H(S[k,k+m-1]*b-sk*b^m+s[k+m])modh.
于是,不断这样计算开始位置右移移位后的字符串子串的哈希值。就可以在O(n)的时间内得到所有位置对应的哈希值,从而可以在O(n+m)时间内完成字符串匹配。在实现时,采用64位无符号整数计算哈希值,并取h=2^64,通过自然溢出省去模运算。
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#include<cmath>
#include<bitset>
using namespace std;

//字符串匹配算法模板
typedef unsigned long long ull;

const ull B=100000007;//哈希的基数

//a是否在b中出现
bool contain(string a,string b)
{
    int al=a.length(),bl=b.length();
    if(al>bl) return false;

    //计算B的al次方
    ull t=1;
    for(int i=0;i<al;i++) t*=B;

    //计算a和b长度为al的前缀对应的哈希值
    ull ah=0,bh=0;
    for(int i=0;i<al;i++) ah=ah*B+a[i];
    for(int i=0;i<al;i++) bh=bh*B+b[i];

    //对b不断右移一位,更新哈希值并判断
    for(int i=0;i+al<=bl;i++){
        if(ah==bh) return true;//b从位置i开始长度为al的字符串等于a
        if(i+al<bl){
            bh=bh*B-b[i]*t+b[i+al];
        }
    }
    return false;
}
//模板

int main()
{
    string s1,s2;
    cin>>s1>>s2;
    if(contain(s1,s2)){
        cout<<1<<endl;
    }
    else{
        cout<<0<<endl;
    }
    return 0;
}
注:这种利用滚动哈希的字符串匹配的算法叫做Rabin-Karp算法。原算法在哈希值相等时,还要用传统的字符串比较算法来判断字符串是否相等。程序设计竞赛中,通常只需比较哈希值,不再做朴素的检查。

不光是右移一位,对于左移一位、左端或右端加长一位或是缩短一位的情况,也能类似处理。
例如,假设要求S的后缀与T的前缀相等的最大长度,可以采用上述类似方法在O(n+m)内求解。
typedef unsigned long long ull;

const ull B=100000007;//哈希的基数

//a的后缀和b的前缀相等的最大长度
int overlap(string a,string b)
{
    int al=a.length(),bl=b.length();
    int ans=0;
    ull ah=0,bh=0,t=1;
    for(int i=1;i<=min(al,bl);i++){
        ah=ah+a[al-i]*t;//a的长度为i的后缀的哈希值
        bh=bh*B+b[i-1];//b的长度为i的前缀的哈希值
        if(ah==bh) ans=i;
        t*=B;
    }
    return ans;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值