zoukankan      html  css  js  c++  java
  • 字符串的一些算法(有时间就更新)

    有关字符串的算法有点多,打算一个一个慢慢来。

    KMP

    这是一个十分神奇的字符串匹配算法,详细过程可在网上搜索。为什么说KMP神奇呢?因为它困扰了我很久很久。days?no,years!

    我就是对这个算法始终有点模糊,学了忘,忘了学,今天又学了一遍有了印象,也写出来了模板,但是可能过了几天又忘了,这种感觉很难讲,我觉得应该要始终备着一个模拟数据来每次模拟一遍,要不然我会对这个算法一直有阴影的。

    例题:GDOI2017 房屋购置

    这道题是对KMP有阴影的另一个原因……,其实就是道裸题,但是我永远不会忘记那一天的痛~

    #include <bits/stdc++.h>
    #define mp make_pair
    using namespace std;
    typedef long long ll;
    inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    const int maxn = 400005;
    const int INF = 0x3f3f3f3f;
    char sss[21][maxn],s1[maxn],s2[maxn];
    int a[21],p[maxn];
    int n,m,i,len_s1,j,k,l,len_s2,y,z;
    int main()
    {
        n=read();
        m=read();
        for (i=1;i<=n;i++)
        {
            scanf("%s",sss[i]+1);
            a[i]=strlen(sss[i]+1);
        }
        while(m--)
        {
            scanf("%s%s",s1+1,s2+1);
            len_s1=strlen(s1+1);
            len_s2=strlen(s2+1);
            j=0;
            for (i=2;i<=len_s1;i++)
            {
                while (s1[i]!=s1[j+1] && j) j=p[j];
                p[i]=(s1[i]==s1[j+1])?++j:j;
            }
            for (k=1;k<=n;k++)
            {
                j=0;
                for (i=1;i<=a[k];i++)
                {
                    if (sss[k][i]=='#') continue;
                    while (s1[j+1]!=sss[k][i] && j) j=p[j];
                    if (s1[j+1]==sss[k][i])
                    {
                        j++;
                        if (j!=len_s1) continue;
                        y=i;
                        l=len_s2;
                        while (l)
                        {
                            if (sss[k][y]!='#') sss[k][y]=s2[l],l--;
                            y--;
                        }
                        l+=len_s1-len_s2;
                        while (l)
                        {
                            if (sss[k][y]!='#') sss[k][y]='#',l--;
                            y--;
                        }
                        j=0;
                    }
                }
            }
        }
        for (i=1;i<=n;i++)
        {
            for (j=1;j<=a[i];j++)
                if (sss[i][j]!='#') cout<<sss[i][j];
            cout<<endl;
        }
    }
    View Code

    Manacher算法

    回文串算法,这个就比较好写,详细过程可在网上搜索。

    Codeforces 1326D1 or D2

    a+b,a是前缀,b是后缀

    1 若a为空或b为空,则直接做一次回文串算法,注意看是否符合前缀或后缀

    2 若a和b都不为空,则先字符串前后进行依次匹配,直到不能匹配时,把中间的字符串再取出来重复1的操作

    #include <bits/stdc++.h>
    #define mp make_pair
    using namespace std;
    typedef long long ll;
    inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    const int maxn = 4000005;
    const int INF = 0x3f3f3f3f;
    int i,j,t,len,len1,len2,Len[maxn];
    string ss,ans1,ans2,s;
    char tmp[maxn];
    void Manacher(string s)
    {
        int i,mx=0,mid=0,x,Left,Right,len;
        len1=len2=0;
        tmp[0]='@';
        tmp[1]='#';
        len=s.size();
        for (i=0;i<=2*len+3;i++)Len[i]=0;
        for (i=1;i<=len;i++)
        {
            tmp[2*i]=s[i-1];
            tmp[2*i+1]='#';
        }
        tmp[2*len+2]='';
        for (i=1;tmp[i];i++)
        {
            if (i<mx)
                Len[i]=min(Len[2*mid-i],mx-i);
            else Len[i]=1;
            while (tmp[i-Len[i]]==tmp[i+Len[i]]) 
                Len[i]++;
            if (Len[i]+i>mx)
            {
                mx=Len[i]+i;
                mid=i;
            }
            x=Len[i]-1;
            if (i%2==0)
            {
                x=(x-1)/2;
                if ((i>>1)-x==1) len1=max(len1,Len[i]-1);
                if ((i>>1)+x==len) len2=max(len2,Len[i]-1);
            }
            else
            {
                x>>=1;
                Left=(i-1)/2;
                Right=(i+1)/2;
                if (Left-x+1==1) len1=max(len1,Len[i]-1);
                if (Right+x-1==len) len2=max(len2,Len[i]-1);
            }
        }
        
    }
    int main()
    {
        t=read();
        while (t--)
        {
            cin>>s;
            len=s.size();
            ans1=ans2="";
            Manacher(s);
            if (len1>len2) ans1=s.substr(0,len1);
                else ans1=s.substr(len-len2);
            i=0,j=len-1;
            while (i<j && s[i]==s[j]) ans2+=s[i],i++,j--;
            if (i<j)
            {
                ss=s.substr(i,j-i+1);
                len=ss.size();
                Manacher(ss);
                if (len1>len2) ans2+=ss.substr(0,len1);
                    else ans2+=ss.substr(len-len2);
            }
            ans2+=s.substr(j+1);
            if (ans1.size()>=ans2.size()) cout<<ans1<<endl;
                else cout<<ans2<<endl;
        }
        return 0;
    }
    View Code

    这里给我自己提个醒:永远别在子函数里面开大数组!永远别在子函数里面开大数组!永远别在子函数里面开大数组!

    字符串hash

    模板

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    using namespace std;
    typedef unsigned long long ull;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 1e5+5;
    const int INF = 0x3f3f3f3f; 
    const int mod = 998244353;
    const int base = 233;
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ull hash[maxn];
    ull pw[maxn];
    void init()
    {
        pw[0]=1;
        for (ri i=1;i<=len;i++) pw[i]=pw[i-1]*base;
    }
    ull gethash(int l,int r)
    {
        return hash[r]-hash[l-1]*pw[r-l+1];
    }
    int main()
    {
        init();
        for (i=1;i<=n;i++)
        {
            scanf("%s",s[i]+1);
            len=strlen(s+1);
        }
        hash[0]=1;
        for (int i=1;i<=len;i++) hash[i]=hash[i-1]*base+(s[i]-'a');
        cout<<gethash(?,?)<<endl;
        return 0;
    }
    View Code

    字典树trie

    根+每一层26个字母组成

    可实现功能:

    第一:词频统计;第二: 前缀匹配;第三:去重

      适用范围:数据量大,重复多,但是数据种类小可以放入内存
      基本原理及要点:实现方式,节点孩子的表示方式

    模板题洛谷P2580

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 500010+100;
    const int N=500010+100;
    const int INF = 0x3f3f3f3f; 
    const int mod = 998244353;
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    int n,m,i;
    char s[maxn];
    ll ans;
    struct trie{
        int a[N][26],cnt[N],t;
        void init(){
            t=0; add();
        }
        int add(){
            memset(a[t],0,sizeof(a[t]));
            cnt[t]=0;
            return t++;
        }
        void insert(const char s[]){
            int k=0;
            for(int i=0;s[i];i++){
                int c=s[i]-'a'; //小写字母
                if(!a[k][c])a[k][c]=add();
                k=a[k][c];
                //son[k]++; //如果要记录子树大小
            }
        //    cnt[k]++;
        }
        ll query(const char s[]){
            ll k=0;
            for(int i=0;s[i];i++){
                int c=s[i]-'a'; //小写字母
                if(!a[k][c])return 0;
                k=a[k][c];
            }
            if (!cnt[k]) 
            {
                cnt[k]=1;
                return 1;
            }
            return 2;
        }
    }tree;
    int main()
    {
        n=read();
        tree.init();
        for (i=1;i<=n;i++)
        {
            scanf("%s",s);
            tree.insert(s);
        }
        m=read();
        for (i=1;i<=m;i++)
        {
            scanf("%s",s);
            ans=tree.query(s);
            if (ans==0) cout<<"WRONG"<<endl;
            else if (ans==1) cout<<"OK"<<endl;
            else cout<<"REPEAT"<<endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    (easy)LeetCode 223.Rectangle Area
    (easy)LeetCode 205.Reverse Linked List
    (easy)LeetCode 205.Isomorphic Strings (*)
    (easy)LeetCode 204.Count Primes
    (easy)LeetCode 203.Remove Linked List Elements
    (easy)LeetCode 202.Happy Number
    (easy)LeetCode 198.House Robber
    (easy)LeetCode 191.Number of 1 Bits
    试题分析
    使用ADO.NET访问数据库
  • 原文地址:https://www.cnblogs.com/Y-Knightqin/p/12535677.html
Copyright © 2011-2022 走看看