• 字符串算法总结


    字符串 Hash

    字符串 Hash( ext{Hash}(a)=sum{a_i x^i} mod{p})。本质是一种进制的思想。

    双 Hash:保证正确性。

    #define mod1 1000000007
    #define mod2 1000000009
    
    ll n,p1[251],p2[251],p1b[251],p2b[251],s1[251],s2[251];
    char c[251];
    ll qpow(ll x,ll y,ll mod) {
        ll res=1;
        while(y) {
            if(y&1) res=res*x%mod;
            x=x*x%mod; y>>=1;
        }
        return res;
    }
    int main() {
        scanf("%lld",&n); scanf("%s",c+1);
        int l1,r1,l2,r2; scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        if((r1-l1)!=(r2-l2)) {printf("No"); return 0; }
        p1[0]=p2[0]=p1b[0]=p2b[0]=1;
        for (int i=1;i<=n;i++) {
            p1[i]=p1[i-1]*347%mod1;
            p2[i]=p2[i-1]*347%mod2;
            if(i==1) {
                p1b[i]=qpow(347,mod1-2,mod1);
                p2b[i]=qpow(347,mod2-2,mod2);
            } else {
                p1b[i]=p1b[i-1]*p1b[1]%mod1;
                p2b[i]=p2b[i-1]*p2b[1]%mod2;
            }
            s1[i]=(s1[i-1]+c[i]*p1[i])%mod1;
            s2[i]=(s2[i-1]+c[i]*p2[i])%mod2;
        }
        ll hash1,hash2,hash3,hash4;
        hash1=(s1[r1]-s1[l1-1]+mod1)%mod1*p1b[l1-1]%mod1;
        hash2=(s2[r1]-s2[l1-1]+mod2)%mod2*p2b[l1-1]%mod2;
        hash3=(s1[r2]-s1[l2-1]+mod1)%mod1*p1b[l2-1]%mod1;
        hash4=(s2[r2]-s2[l2-1]+mod2)%mod2*p2b[l2-1]%mod2;
        if(hash1==hash3&&hash2==hash4) printf("Yes %lld %lld %lld %lld",hash1,hash3,hash2,hash4)
        else printf("No %lld %lld %lld %lld",hash1,hash3,hash2,hash4);
        return 0;
    }
    

    Knuth–Morris–Pratt (KMP)

    字符串匹配问题:给出文本串模式串,求出模式串在文本串中所有出现的位置。

    利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置。

    (P_k=P_j) 时,有 ( ext{next}(j+1)= ext{next}(j) + 1)

    (P_k e P_j) 时,有 (k= ext{next}(k))

    推荐:学习 KMP 看 Link link,复习 KMP 看 Link link

    char s[N], p[M];
    int sl, pl;
    
    int nex[M];
    
    inline void getnex() { // 求前缀数组
    	int k=-1, j=0; nex[0]=-1;
    	while (j<pl) {
    		if (k==-1 || p[j]==p[k]) { // 当两个字符相等时要跳过
                if (p[++j]==p[++k]) nex[j]=nex[k]; else nex[j]=k;
            } else k=nex[k];
    	}
    }
    
    inline void kmp() {
    	int i=0, j=0; // i主串的位置,j模式串的位置
    	while (i<sl && j<pl) {
    		if (j==-1 || s[i]==p[j]) i++, j++; else j=nex[j]; // 当j为-1时,要移动的是i,当然j也要归0
    		if (j==pl) printf("%d
    ", i-pl+1), j=nex[j];
    	}
    }
    
    int main() {
    	scanf("%s", s); scanf("%s", p);
    	sl=strlen(s); pl=strlen(p);
    	getnex();
    	kmp();
    	return 0;
    }
    

    字典树 Trie

    按前缀分类。

    具体形态为一棵有根树,每条边上记录一个字符,从根到一个节点路径上所有字符接起来即这个节点所代表的字符串。实现时,我们对每个节点维护与字符集一一对应的儿子集合。插入字符串时,依次遍历串中每个字符,并从根开始走每个字符对应的儿子,如果不存在则新建对应节点。时间复杂度为 字符串总长,空间复杂度为 字符串总长 ( imes) 字符集。

    空间优化:用 map 来存储儿子优化空间,代价是时间复杂度多一个 log。


  • 相关阅读:
    ssh协议运用
    linux系统安装oracle遇到的问题
    Qt中mysql编译出错问题
    winform控件添加鼠标事件
    Linux网络编程
    多线程
    守护进程
    openssl 生成自签CA和pkcs12证书
    Linux CPU使用率获取 c
    temp
  • 原文地址:https://www.cnblogs.com/greyqz/p/string.html
Copyright © 2020-2023  润新知