字符串前缀同构,最大长度

时间:2014-06-20 01:10:31

标签: c++ string algorithm

字符串s1和s2是同构的意思 - 如果我们将两个弦组成环,则两个环是相同的。 示例:“abcd”和“cdab”是同构的 “abcd”和“dcba”不是isomrphic “cdab”和“abdc”也不是。

现在你有两个字符串,输出一个最大长度,使两个字符串的前缀长度同构。

示例:“abcdx”和“cdabz” 最大长度为4。

时间复杂度尽可能低。

PS:两个字符串的长度相同

这是一个错误的解决方案,但通过了所有测试

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <stack>
#include <string>
#include <algorithm>
#define MAXN 2000001
#define MOD 100000007
using namespace std;
char str1[MAXN],str2[MAXN];
int next1[MAXN],next2[MAXN],n;
int p[27][26],ct=0;
void getnext(char *s1,char *s2,int* next){
    next[0]=0;
    int pos=2,cnd=0;
    while(pos<n){
        if(s1[pos-1]==s2[cnd])
        {++cnd;next[pos]=cnd;++pos;}
        else if(cnd>0)cnd=next[cnd];
        else {next[pos]=0;++pos;} 
    }
}
void getpri(){
    int cnt=0;
    for(int i=2;cnt<26;i++){
        bool flag=true;
        for(int j=2;j*j<=i;j++)if(i%j==0){flag=false;break;}
        if(flag)p[cnt][ct++]=i;
        if(ct==26)++cnt,ct=0;
    }
}
string s1,s2,str;
bool check(int p){
    str=string(s1.substr(0,p)+s1.substr(0,p));
    int res=str.find(s2.substr(0,p));
    if(res!=-1)return true;
    return false;
}
stack<int>stk;
int main()
{
    freopen("beyond.in","r",stdin);
    freopen("beyond.out","w",stdout);
    getpri();
    scanf("%d\n%s\n%s",&n,str1,str2);
    s1=str1;s2=str2;
    getnext(str1,str2,next1);
    getnext(str2,str1,next2);
    int ans=0;
    long long h1=1,h2=1;
    for(int i=0;i<n;i++){
        if(i>0){
            h1=(h1*p[str1[i-1]-'a'][str1[i]-'a'])%MOD;
            h2=(h2*p[str2[i-1]-'a'][str2[i]-'a'])%MOD;
        }
        if(next1[i+1]+next2[i+1]>=i&&
        h1*p[str1[i]-'a'][str1[0]-'a']==
        h2*p[str2[i]-'a'][str2[0]-'a'])stk.push(i+1);
    }
    while(!stk.empty()){
        if(check(stk.top())){ans=stk.top();break;}
        stk.pop();
    }
    printf("%d\n",ans);
    return 0;
}

PS:这个问题可能没有O(n)解决方案,O(n)解决方案仅在其中一个字符串是最小表示时才存在。

2 个答案:

答案 0 :(得分:1)

我认为您可以使用以下方法解决此问题: -

1. Find longest prefix for each substring string1[0 to i] which are also suffix for substring string2[0 to i] and visa versa.Lets call them lps1[i] & lps2[i].
2. if lps1[i] + lps2[i] == i+1 then prefix upto i is isomorphic

example :-

string1 = "abcdx" 
string2 = "cdabz"

lps1[0] = 0
lps2[0] = 0

lps1[1] = 0
lps2[1] = 0

lps1[2] = 1
lps2[2] = 1

lps1[3] = 2
lps2[3] = 2

lps1[4] = 0
lps2[4] = 0

lps1[3] + lps2[3] = 4 = 3+1 hence the largest prefix isomorphic is of length 3+1 = 4

现在的主要问题是如何有效地找到lps?

KMP使用类似的算法在O(|S|)中构建表格。检查部分表格构建算法它基本上构造了lps for string,但你可以替换

if W[pos - 1] = W[cnd] 

按: -

if string1[ pos-1 ] = string2[cnd]  

with some corner checks like cnd < string2.length

时间复杂度: -

Build LPS arrays : O(|S1|+|S2|)

check isomorphic prefix : O(min(|S1|,|S2|)

这是c ++实现: -

#include<cstdio>
#include<cstring>



void ComputeLPS(char* str1,char* str2,int n,int LPS[]) {
  LPS[0] = 0;
  int len = 0,i=1;
  while(i<n){
    if(str2[len]==str1[i]) {
      len++;
      LPS[i] = len;
      i++;
    }
    else {

      if(len!=0) {
    len = LPS[len-1];
      }
      else {
    LPS[i] = 0;
    i++;
      }
    }

  }



}


int isomorphic_prefix(char* str1,char* str2) {

  int n = strlen(str1);
  int lps1[n];
  int lps2[n];

  ComputeLPS(str1,str2,n,lps1);
  ComputeLPS(str2,str1,n,lps2);
  int max = 0;

  for(int i = 0;i < n;i++) {

    int k = lps1[i]+lps2[i];

    if(k==i)
      max = i+1;
  }

  return max;

}



int main() {

  char str1[100];
  char str2[100];

  gets(str1);
  gets(str2);

  printf("max isomorphic prefix : %d",isomorphic_prefix(str1,str2));

  return 0;
}

答案 1 :(得分:0)

我在这个问题上考虑了很多,我得到的是这个问题比我们想象的要简单。根据我的说法,您可以通过在循环排列中保留(a,b)建议链接a之前的b链接来解决此问题。如果string1string2的所有链接相同,则表示它们是相同的循环排列。这里的诀窍是为string1提供链接(a,b) +1 ,为string2提供 -1 ,因此如果所有链接都相同则所有对{{1}将没有计数。这可以通过使用对(a,b)来实现,其中arr[26][26] for all alphabet pairs表示网络链接的数量arr[a][b]存在两种排列的差异。

证明循环排列可以完全用链接集来表示: -

考虑循环排列: -

X0-&GT; X1-&GT; X2-&GT; X3-&GT; X0

在链接集中的表示形式: - (a,b)

声称这组链接始终只代表上述排列。

我们将通过矛盾来证明这一点。让我们假设有一个不同的循环排列,可以用同一组链接表示。然后可以通过交换两个字符形成不同的圆形排列。

我们交换x1和x3然后我们得到排列: -

(x0,x1),(x1,x2),(x2,x3),(x3,x0)

因此,矛盾证明了一组链接只代表一个循环排列。

使用此结果我编写了以下c ++实现: -

x0->x3->x2->x1->x0

**set of links :-** (x0,x3),(x3,x2),(x2,x1),(x1,x0)

There are two possibilities :-

1. x1 == x3 then new permutation is same as old so there is contradiction here.
2. x1 != x3 then (x0,x3) != (x0,x1) , (x3,x0) != (x1,x0) hence again there is contradiction.


This result can be extended to multiple swaps.

时间复杂度: -

正如您在实现中所看到的那样,它是一个 O(N)的单循环,内部进行常数计算,因此它是 O(N)