字符串的一些算法(有时间就更新)

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

 

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]='\0';
    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

 

posted @ 2020-04-30 18:05  Y-KnightQin  阅读(190)  评论(0编辑  收藏  举报