POJ3167&UVA455&UVA11022&UVA11452&codeforces MUH and Cube Walls(KMP)

KMP学习文章,很好懂的两篇文章
oiwiki 一篇
还有这篇

KMP

一种高效的字符匹配算法

前缀函数定义

给定一个长度为n的字符串 s s ,其前缀函数是一个数组 π π
π [ i ] π[i] 表示其中 s [ 0 , 1 , 2 i ] s[0,1,2\cdots i] 为既是子串 的前缀同时也是该子串的后缀的最长真前缀(proper prefix)长度。真前缀代表即是子串的前缀但不等于子串
例如
s = " a b c a b d a b c " s="a b c a b d a b c"
π = " 000120123 " π="000120123"
求前缀函数代码

vector<int> prefix(string s) {
  int n = (int)s.length();
  vector<int> pi(n);
  for (int i = 1; i < n; i++) {
    int j = pi[i - 1];
    while (j > 0 && s[i] != s[j]) j = pi[j - 1];
    if (s[i] == s[j]) j++;
    pi[i] = j;
  }
  return pi;
}

重要的是要怎么用的问题了
问题一:给定一个字符串 t t s s ,问 s s 是不是 t t 的一个子串
解决:构造成一个字符串 s + s+ ’#’ + t +t (’#'为没有在字符串 s s t t 中出现的, 丢进去求前前缀和就行啦。
n = s . l e n g t h ( ) n=s.length() 则存在前缀函数等于 n n 的就说明 s s t t 的一个子串

问题二:压缩字符串

给定一个长度为 n n 字符串 s s ,对其压缩到最短,即把 s s 分成 m m 份等份相同的字符串,如 s = " a b c a b c a b c " s="abcabcabc" 可以压缩为 " a b c " "abc"

k = n π n 1 ) k=n-π(n-1) k n k|n s [ 0 , 1 , 2 k 1 ] s[0,1,2\cdots k-1] 为最短的压缩后的字符串

否则 s s 只能压缩成 s s

问题三:一个字符串中本质不同子串的数目

给定一个长度为 n n 的字符串 s s ,计算其本质不同子串的数目

以迭代的办法来求,若知道了当前的本质不同子串的数目,在其末尾加一个字符c后重新计算该数目的办法

构造字符串 t = s + c t=s+c ,并将其反转~ t t ,现在我们的任务变为计算有多少~ t t 的前缀未在 的其余任何地方出现。如果我们计算了 ~ t t 的前缀函数最大值 ,那么最长的出现在 s s 中的前缀其长度为 π m a x π_{max} 。自然的,所有更短的前缀也出现了。

因此,当添加了一个新字符后新出现的子串数目为 s + 1 π m a x |s|+1-π_{max}

POJ3167

题意:
意思是给你一个 1 S 1-S 的序列,然后再给你1-k等级的子序列,问你有多少个符合的子串,并输出各自子串的起始位置,如例子,输入 N , K , S N,K,S 9 , 6 , 10 9,6,10 然后输入串 t t 5 , 6 , 2 , 10 , 10 , 7 , 3 , 2 , 9 5 ,6, 2, 10, 10, 7 ,3, 2, 9 然后输入子串等级 s s 1 , 4 , 4 , 3 , 2 , 1 1,4, 4, 3, 2, 1 ,子串的意思是等级越高数越大,相同等级的数要一样,所以子串可以变成 2 10 10 7 3 2
题解:将原序列 1 S 1--S 看成 S S 个等级,记录到 t [ i ] t[i] 每个等级的个数,
则两个子串相同的条件就是对于每个字符,比它等级小的字符个数相同且相同字符等级的个数相同即可
由于S才25,所以可以暴力存一下,利用一下前缀和即可判断两个字符等不等价,然后就变成了问题一了。对于’#'处小心处理即可

#include<algorithm>
#include<iostream>
#include<cstring>
#define ll long long
#define endl '\n'
const int MX=2e5+7;
using namespace std;
int A[MX],top;
struct node
{
    int pre[26],val;
    void get(){cin>>val;}
}pa[MX],pb[MX];
int pi[MX],K;
bool cmp(node a,node b,int i,int j)
{
    if(i>=K)return false;
    if(j-i-1<K&&j>K)return false;
    int x=b.val,y=a.val,ans=0;
    for(int k=1;k<x;k++)ans+=b.pre[k]-pa[j-i-1].pre[k];
    int res=0;
    for(int k=1;k<y;k++)res+=a.pre[k];
    if(ans!=res)return false;
    ans=b.pre[x]-pa[j-i-1].pre[x];
    res=a.pre[y];
    if(ans!=res)return false;
    return true;
}
void prefix(node *pa, int n) {
  pi[0] = 0;
  for (int i = 1; i < n; i++) {
    int j = pi[i - 1];
    while (j > 0 && !cmp(pa[j],pa[i],j,i)) j = pi[j - 1];
    if (cmp(pa[j],pa[i],j,i)) j++;
    pi[i] = j;
  }
}
int main()
{
  ios::sync_with_stdio(0),cin.tie(0);
  int N,S;
  cin>>N>>K>>S;
  for(int i=0;i<N;i++)pb[i].get();
  for(int i=0;i<K;i++)pa[i].get();
  pa[K].val=30;
  for(int i=K+1,e=0;e<N;e++)pa[i+e]=pb[e];
  memset(pa[0].pre,0,sizeof(pa[0].pre));
  pa[0].pre[pa[0].val]=1;
  for(int i=1;i<=K+N;i++)
  {
      for(int j=1;j<=S;j++)pa[i].pre[j]=pa[i-1].pre[j];
      pa[i].pre[pa[i].val]++;
  }
   prefix(pa,K+N+1);
   for(int i=K+1;i<K+N+1;i++)if(pi[i]==K)A[++top]=i-2*K+1;
   cout<<top<<endl;
   for(int i=1;i<=top;i++)cout<<A[i]<<endl;
}

UVA455

即是问题二了

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=4e2+7;
const int mod=9901;
using namespace std;
int p[MX],k[MX];
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
vector<int> prefix_function(string s) {
  int n = (int)s.length();
  vector<int> pi(n);
  for (int i = 1; i < n; i++) {
    int j = pi[i - 1];
    while (j > 0 && s[i] != s[j]) j = pi[j - 1];
    if (s[i] == s[j]) j++;
    pi[i] = j;
  }
  return pi;
}
int main()
{
  ios::sync_with_stdio(0),cin.tie(0);
  string s;
  int t;
  cin>>t;
  while(t--){
  cin>>s;
  int n=s.length();
  vector<int>pi=prefix_function(s);
  int k=n-pi[n-1];
  if(n%k!=0)k=n;
  cout<<k<<endl;
  if(t>=1)cout<<endl;
  }

}

UVA11022

题意:对长度为 n n 的字符串(字符串下标从0开始)
区间dp+KMP
d p [ i ] [ j ] dp[i][j] 为字符串 s [ i , i + 1 , i + 2 j ] s[i,i+1,i+2\cdots j] 压缩后最短的长度
则转态转移为
p r e f i x [ i ] [ j ] prefix[i][j] s [ i , i + 1 , i + 2 , j ] s[i,i+1,i+2,\cdots j] 按问题二压缩后的长度
d p [ i ] [ j ] = m i n ( d p [ i ] [ k ] + d p [ k + 1 ] [ j ] d p [ i ] [ i + p r e f i x [ i ] [ j ] 1 ] ) dp[i][j]=min(dp[i][k]+dp[k+1][j],dp[i][i+prefix[i][j]-1])

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=4e2+7;
const int mod=9901;
using namespace std;
int p[MX],k[MX];
int dp[MX][MX],prefix[MX][MX];
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
void prefix_function(char*s,int k) {
  int n = strlen(s);
  vector<int> pi(n);
  pi[0]=0;
  for (int i = 1; i < n; i++) {
    int j = pi[i - 1];
    while (j > 0 && s[i] != s[j]) j = pi[j - 1];
    if (s[i] == s[j]) j++;
    pi[i] = j;
  }
  for(int i=0;i<n;i++)
  {
      int m=i+k;
      if((i+1)%(i+1-pi[i])==0)
        prefix[k][m]=i+1-pi[i];
      else prefix[k][m]=i+1;
  }
  return ;
}
int main()
{
  //ios::sync_with_stdio(0),cin.tie(0);
  char s[100];
  int t;
  while(cin>>s&&s[0]!='*'){
  int n=strlen(s);
  for(int i=0;i<n;i++)
  {
      prefix_function(s+i,i);
  }
  for(int i=0;i<n;i++)dp[i][i]=1;
  for(int len=2;len<=n;len++)
  {
      for(int i=0;i+len<=n;i++)
      {
          int j=i+len-1;
          dp[i][j]=len;
          for(int k=i;k<j;k++)
          {
              dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
          }
          dp[i][j]=min(dp[i][j],dp[i][i+prefix[i][j]-1]);
      }
  }
  cout<<dp[0][n-1]<<endl;
  }

}

UVA11452

题解:用KMP找到前两段的末尾部分,也就找到了循环节,则从那开始往后补齐八个字符即可

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=4e2+7;
const int mod=9901;
using namespace std;
int p[MX],k[MX];
int dp[MX][MX],prefix[MX][MX];
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
vector<int> prefix_function(string s) {
  int n = s.length();
  vector<int> pi(n);
  pi[0]=0;
  for (int i = 1; i < n; i++) {
    int j = pi[i - 1];
    while (j > 0 && s[i] != s[j]) j = pi[j - 1];
    if (s[i] == s[j]) j++;
    pi[i] = j;
  }
  return pi;
}
int main()
{
  ios::sync_with_stdio(0),cin.tie(0);
  //char s[100];
  int t;
  cin>>t;
  string s;
  while(t--){
  cin>>s;
  int n=s.length();
  //cout<<n<<endl;
  vector<int>pi=prefix_function(s);
  int tmp=0;
  //cout<<tmp<<endl;
  for(int i=n-1;i>=0;i--)
  {
      if((i+1)%(i+1-pi[i])==0&&(i+1)/(i+1-pi[i])==2)
      {
          tmp=i;
          break;
      }
  }
  int m=tmp/2+1;
  for(int i=0;i<8;i++)
  {
      cout<<s[(n+i)%m];
  }
    cout<<"..."<<endl;
  }

}

MUH and Cube Walls

对于两个字符串匹配的条件是对于每一个字符,相邻字符差相等即可匹配,然后就变成了问题二了,用KMP搞一下就行了

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=4e5+7;
const int mod=9901;
using namespace std;
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
int a[MX],b[MX];
void prefix_function(int*s,int w,int n) {
  //int n = s.length();
  vector<int> pi(n);
  pi[0]=0;
  for (int i = 1; i <n; i++) {
    int j = pi[i - 1];
    while (j > 0 && s[i] != s[j]) j = pi[j - 1];
    if (s[i] == s[j]||(j==0&&i!=w)) j++;
    pi[i] = j;
  }
  int ans=0;
 // cout<<w<<" "<<n<<endl;
  for(int i=w+1;i<n;i++)
  {
     // cout<<pi[i]<<endl;
      if(pi[i]==w)ans++;
  }
  cout<<ans<<endl;
  return ;
}
int main()
{
  ios::sync_with_stdio(0),cin.tie(0);
  int n,w;
  cin>>n>>w;
  for(int i=0;i<n;i++)
  {
      cin>>a[i];
  }
  for(int i=0;i<w;i++)
    cin>>b[i];
  b[w]=2e9;
  for(int i=w,e=1;e<=n;e++)
  {
      b[i+e]=a[e-1];
  }
  for(int i=w+n;i>=1;i--)
  {
      if(i==w||i==w+1)continue;
      b[i]=b[i]-b[i-1];
   //   cout<<"i="<<i<<" b="<<b[i]<<endl;
  }
  prefix_function(b,w,w+n+1);
  //cout<<
}
发布了109 篇原创文章 · 获赞 35 · 访问量 6002

猜你喜欢

转载自blog.csdn.net/weixin_43965698/article/details/104249792