zoukankan      html  css  js  c++  java
  • 字符串学习笔记

    哈希

    • 哈希理解起来就相当于用一种映射方式,以实现快速对比字符串或者其他东西(比如图)的功能,有时候利用技巧还能实现字典序大小的比较或者反映射操作。
    • 其思想是非常重要而且容易理解的,关键在于hash方法以及编程上的技巧。
    • 取一固定值(P),把字符串看作(P)的进制数,代表每种字符。再取一固定值(M),求出(P)进制位对(M)的余数,作为该字串的哈希值。
    • 我们取(P=233)(P=13331),此时(Hash)值产生冲突的概率很低,只要(Hash)值相等,我们就可以认为原字符串是相等的。
    • 我们通常取(M=2^64),即直接使用unsigned long long类型存储这个(Hash值),这种类型自动对(2^64)取模,避免低效的mod运算。

    实现方法

    • 前缀和是hash的常见用法

    方法一

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+100;
    const int base=233;
    char s[maxn];
    int poww[maxn],hash[maxn];
    int main(){
        while(cin>>s+1){
            int len=strlen(s+1);
            poww[1]=233;
            for(int i=2;i<=len;i++)
                poww[i]=poww[i-1]*base;
            hash[1]=s[1];
            for(int i=2;i<=len;i++)
                hash[i]=hash[i-1]*base+s[i];
        }
    }
    

    方法二

    • 把字符串变成数字
    #define ull unsigned long long
    ull gethash(string s){
        ull tmp=0;
        for(int i=0;i<s.size();i++)
            tmp=tmp*233+(s[i]-'a');
        return tmp;
    }
    

    一道例题

    题目

    已知字符串A与B,有n组询问,每次询问给l,r,s,t,要求O(1)判断A[l…r]与B[s…t]是否相同。

    题解

    • 既然已经求得两个字符串的hash数组,就直接取A[l..r]的哈希值与B[s…t]的哈希值进行比较即可。
    • 那么如何更加方便的比较呢?
    • 我们使用 (hash[i]=hash[i-1]*10+s[i]) 的方法进行哈希得哈希数组(hasha,hashb)
    • 然后我们发现一个问题:
      • 举一例子:

      (s1: 1 2 3 4 5 1)
      (s2: 2 3 4 1 1 2)

      • (s1[2…4])(s2[1…3]) 是相同的,但我们发现,(hasha[4]-hasha[1])(hashb[3]-hashb[0])并不相同,为什么呢?
      • 回忆我们的哈希过程,发现我们要乘上一个 (poww[i]),这样我们求出来的才是真正的哈希,即(hash[r]-hash[l-1]*pow[i])

    code

    while(n--){
        cin>>l>>r>>s>>t;
        len=r-l+1;
        if(hasha[r]-hasha[l-1]*poww[len] == hashb[s]-hahsb[t-1]*poww[len])
            puts("YES");
        else puts("NO");
    }
    

    再来几道例题

    manachar

    普及知识

    • 回文串:abcba(√),abba(√),abda(×)(就是正着看和倒着看相同的串)
    • 子串≠子序列(子串是连续的,子序列可不连续)

    一道例题

    求一个字符串中最长回文子串长度。
    例:字符串:abbababa   最长回文子串:ababa

    题解

    • manacher是一种暴力的优化,可以用O(n)的时间解决上述问题,但是我真的没怎么见过这玩意派上用场
    • 可以学一下回文自动机,那个的作用要比这个大得多...
    • 因为偶回文串比较难处理(不是以某个字符为中心),所以首先我们要通过在每个相邻字符中间插入一个相同的字符(比如’|’,一定要是没有出现过的),这样预处理操作一发后我们会发现所有的回文串都是奇回文串,并且它也不会影响答案。
    • 把预处理后的字符串叫做S
    • 最后的出来的答案就是 S的最长回文子串长+1
    • 复杂度O(n)也是容易理解的,因为R只有一次从1到n,以及i只有一次从1到n,而且二者是并列关系。
    • 此种算法心中有印象即可,十分不常考。

    code

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e8+100;
    char a[maxn<<1];
    int f[maxn<<1];
    int main(){
        int cnt=0;a[0]='~';a[++cnt]='|'; char ch; int ans=0;
        while((ch=getchar())>='a' && ch<='z') a[++cnt]=ch,a[++cnt]='|';
        for(int i=1,r=0,mid=0;i<=cnt;i++){ //mid中心,i现在的点,f[]半径 
            if(i <= r) f[i]=min(f[mid*2-i],r-i+1);
            while(a[i-f[i]]==a[i+f[i]]) f[i]++;
            if(f[i]+i>r) r=f[i]+i-1,mid=i;
            if(f[i]>ans) ans=f[i];
        }
        printf("%d
    ",ans-1);
        return 0;
    }
    

    推荐例题

    有兴趣可以做一做洛谷板子题

    KMP

    讲解

    code

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+100;
    char a[maxn],b[maxn];
    int fail[maxn];
    int main(){
    	cin>>a+1>>b+1;
    	int lena=strlen(a+1),lenb=strlen(b+1);
    	for(int i=2,j=0;i<=lenb;i++){
    		while(j && b[i] != b[j+1]) j=fail[j];
    		if(b[i] == b[j+1]) j++;
    		fail[i]=j;
    	}	
    	for(int i=1,j=0;i<=lena;i++){
    		while(j && a[i] != b[j+1]) j=fail[j];
    		if(a[i] == b[j+1]) j++;
    		if(j==lenb){
    			cout<<i-lenb+1<<endl;
    			j=fail[j];
    		}
    	}
    	for(int i=1;i<=lenb;i++)
    		cout<<fail[i]<<" ";
    	
    }
    

    推荐题目:落谷板子题

  • 相关阅读:
    Java流程控制语句
    Linux文件过滤及内容编辑处理
    Java运算符优先级
    Java位运算基础知识
    【Linux】【FastDFS】FastDFS安装
    【Linux】【redis】redis安装及开启远程访问
    【Linux】【sonarqube】安装sonarqube7.9
    【Linux】【PostgreSQL】PostgreSQL安装
    【Linux】【maven】maven及maven私服安装
    【Linux】【jenkins】自动化运维七 整合sonarqube代码审查
  • 原文地址:https://www.cnblogs.com/hellohhy/p/13346389.html
Copyright © 2011-2022 走看看