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

KMP學習文章,很好懂的兩篇文章
oiwiki 一篇
還有這篇

KMP

一種高效的字符匹配算法

前綴函數定義

給定一個長度爲n的字符串ss,其前綴函數是一個數組ππ
π[i]π[i]表示其中 s[0,1,2i]s[0,1,2\cdots i] 爲既是子串 的前綴同時也是該子串的後綴的最長真前綴(proper prefix)長度。真前綴代表即是子串的前綴但不等於子串
例如
s="abcabdabc"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;
}

重要的是要怎麼用的問題了
問題一:給定一個字符串ttss,問ss是不是tt的一個子串
解決:構造成一個字符串s+s+’#’+t+t(’#'爲沒有在字符串sstt中出現的, 丟進去求前前綴和就行啦。
n=s.length()n=s.length()則存在前綴函數等於nn的就說明sstt的一個子串

問題二:壓縮字符串

給定一個長度爲nn字符串ss ,對其壓縮到最短,即把ss分成mm份等份相同的字符串,如s="abcabcabc"s="abcabcabc"可以壓縮爲"abc""abc"

k=nπn1)k=n-π(n-1)knk|ns[0,1,2k1]s[0,1,2\cdots k-1]爲最短的壓縮後的字符串

否則ss只能壓縮成ss

問題三:一個字符串中本質不同子串的數目

給定一個長度爲nn的字符串ss ,計算其本質不同子串的數目

以迭代的辦法來求,若知道了當前的本質不同子串的數目,在其末尾加一個字符c後重新計算該數目的辦法

構造字符串t=s+ct=s+c,並將其反轉~tt ,現在我們的任務變爲計算有多少~tt 的前綴未在 的其餘任何地方出現。如果我們計算了 ~tt 的前綴函數最大值 ,那麼最長的出現在 ss中的前綴其長度爲 πmaxπ_{max}。自然的,所有更短的前綴也出現了。

因此,當添加了一個新字符後新出現的子串數目爲s+1πmax|s|+1-π_{max}

POJ3167

題意:
意思是給你一個1S1-S的序列,然後再給你1-k等級的子序列,問你有多少個符合的子串,並輸出各自子串的起始位置,如例子,輸入N,K,SN,K,S9,6,109,6,10然後輸入串tt5,6,2,10,10,7,3,2,95 ,6, 2, 10, 10, 7 ,3, 2, 9然後輸入子串等級ss1,4,4,3,2,11,4, 4, 3, 2, 1,子串的意思是等級越高數越大,相同等級的數要一樣,所以子串可以變成 2 10 10 7 3 2
題解:將原序列1S1--S看成SS個等級,記錄到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

題意:對長度爲nn的字符串(字符串下標從0開始)
區間dp+KMP
dp[i][j]dp[i][j]爲字符串s[i,i+1,i+2j]s[i,i+1,i+2\cdots j]壓縮後最短的長度
則轉態轉移爲
prefix[i][j]prefix[i][j]s[i,i+1,i+2,j]s[i,i+1,i+2,\cdots j]按問題二壓縮後的長度
dp[i][j]=min(dp[i][k]+dp[k+1][j]dp[i][i+prefix[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<<
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章