字符串 专题

 

今天复习字符巛 还剩下一点时间  我先 打一下模板

$ 1$  最小表示法

贪心 首先把 $ S $ 复制一遍成一个环 

然后定义两个指针  i j 从 i j 开始一次扩展 字符巛 的长度  即  从$0$ 的长度开始扩展到 $n-1$  ($k $最大到 $n$ >- 停止循环 找到答案)

然后比较其中是否相等  

如果不相等就跳过循环 

判断 当前 $S[i+k]$ 与$S[j+k]$ 的 大小 因为中间的字符串 都相等 所以 指针可以跳到  $x+k+1$ 处 $[x|i,j]$ 

然后因为 最小的指针一定不会移动到最大的指针的后面所以 答案为 $min(i,j)$

$ i,j $只循环到 n 的位置 因为以后都是一样的 了 

 

code;

int i=1,j=2;
int k=0;
while(i<=n&&j<=n)
{
        for(int k=0;k<n;k++)
    {
      if(S[i+k]!=S[j+k]) break;
    }
    if(k==n) break;
       if(s[i+k]>s[j+k]) i=i+k+1;
       else
    {
      if(s[i+k]<s[j+k]) j=j+k+1;
    }
    if(i==j) j++;
}          
cout<<min(i,j);

 

 

2 .01 字典树

 作用 : 寻找异或最大值 

方法 :

 

   插入: 每位每位地找 没有就扩展 扩展完之后 就插入

code:

void inset(int v)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int k=(sum[v]>>i)&1;
        if(!ch[u][k])
        {
            ch[u][k]=tot++;
        }
        u=ch[u][k];
    }
    val[u]=sum[v];
}

 

 寻找:从最高位 开始 搜寻 先找 不同的 后找相同的 注意 先插入 $sum[0]$ 保证有解 

      如果 找不到 就一定没有

code:

int find(int v)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int k=(v>>i)&1;
        if(ch[u][k^1]) u=ch[u][k^1];
        else
        u=ch[u][k];
    }
    return val[u];
}

 

 

$3$ 状态很差,但是还是坚持来写下博客啦

KMP

作用 匹配或者最长字串

首先 fail数组 记的是 前i位 的最长前缀和后缀 

求fail 数组首先得与自己比对  注意得从第二位开始 求出回退值

code:


        fail[0]=-1;
        int j=-1;
        for(int i=1;i<s[1].size();i++) 
        {
            while(j>-1&&(s[1][j+1]!=s[1][i])) j=fail[j];
            
            if(s[1][j+1]==s[1][i]) j++;
            fail[i]=j;
        }

求匹配   匹配使得前后缀一样 就很强 从第一位开始

int j=-1;
    int ans=0;
    fail[0]=-1;
    for(int i=0;i<s[v].size();i++)
    {
        while(j>-1&&(s[1][j+1]!=s[v][i])) {
            ans=max(ans,j);
        j=fail[j];}
        
        if(s[1][j+1]==s[v][i]) j++;
     if(j==m)
     {
     
        ans=max(j+1,ans);

     }
} return ans;

$@$求最长公共字串匹配

找一个串 然后挨个匹配 找最小值 然后删除第一个元素 再次找最小值 最后取max 

介绍一下动态数组的erase神操作

 

#include<bits/stdc++.h>
using namespace std;
int n;
string  s[8];
int fail[3000];
int  kmp(int v)
{
    int j=-1;
    int ans=0;
    fail[0]=-1;
    for(int i=0;i<s[v].size();i++)
    {
        while(j>-1&&(s[1][j+1]!=s[v][i])) {
            ans=max(ans,j);
        j=fail[j];}
        
        if(s[1][j+1]==s[v][i]) j++;

            ans=max(ans,j+1);
    }
    return ans;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
    }
    int aans=0;
    while(s[1].size())
    {
    int ans=100000000;
        fail[0]=-1;
        int j=-1;
        for(int i=1;i<s[1].size();i++) 
        {
            while(j>-1&&(s[1][j+1]!=s[1][i])) j=fail[j];
            
            if(s[1][j+1]==s[1][i]) j++;
            fail[i]=j;
        }
    for(int i=2;i<=n;i++)
    {
        
        ans=min(ans,kmp(i));
        
    }
       aans=max(aans,ans);
       s[1].erase(s[1].begin());
    }
    cout<<aans;
}

  

 

$@@$结论 字符串乘方 i%(i-fail[i])==0 那么一定有乘方 个数为$i/(i-fail[i])$

 $4$马拉车

首先初始化 把回文串都变成奇数回文串

类似于这样

void init()
{
    S[0]='#';
    S[1]='#';

    for(int i=0;i<strlen(s);i++)
    {
        S[++tot]=s[i];
        S[++tot]='#';
    }
    S[++tot]='\0';
    
}

 

一共由两种大情况 i 在已经更新的回文串maxlen 的右边或左边

对于i在左边的情况 $r[i] =min|{r[2*p-i],maxlen-i+1}|$ 其中$2*p-i$是$i$ 关于$p $对称的回文对应位置

对于i 在右边的情况 $r[i]=1$ 

     if(i<maxlen)
           r[i]=min(maxlen-i+1,r[2*P-i]);
        else
           r[i]=1;

 

然后挨着扩展

类似于这样

 while(S[i-r[i]]==S[i+r[i]])r[i]++;

最后更新maxlen 和p 的$val$

     if(i+r[i]-1>maxlen)
         {
             maxlen=i+r[i]-1;
             P=i;
         }

 

注意到r数组位是最长回文半径(包括自己)

$r[i]-1$ 是原字符串的回文长度

         

string 类型可以直接比较  字典序

strcpy strcmp strlen 字符数组的函数

posted @ 2019-08-12 11:43  ALEZ  阅读(216)  评论(0编辑  收藏  举报