检查是否有同构子串


12

这个问题是“ 检查单词是否同构”的扩展, 并将其第一部分复制以给出同构的定义。

两个词是同构,如果他们有字母重复相同的模式。例如,两个ESTATEDUELED都有模式abcdca

ESTATE
DUELED

abcdca

因为字母1和6相同,所以字母3和5相同,仅此而已。这也意味着单词由替换密码(此处与match)相关E <-> D, S <-> U, T <-> E, A <-> L

给定两个字符串X和Y,且X的长度不超过Y,任务是确定是否存在Y的子字符串,该子字符串与X同构。

输入: a..zA..Z的两个非空字母字符串,每行一个字符串。这将来自标准输入。

输出第二个字符串的子字符串,如果存在则与第一个字符串同形。否则输出“否!”。

规则您的代码必须在输入的总长度内线性运行。

分数分数是代码中的字节数。最少的字节数获胜。


输入和输出示例

adca
ddaddabdaabbcc

dabd

小费

对于这个问题,至少有一个不是那么复杂,实际上是快速且线性的时间解决方案。


@AlexA。我认为有人告诉我,如果您限制运行时间/复杂度,那么它应该是代码挑战。如果那是错误的话,我很乐意更改它。

7
如果运行时受规则限制且不影响评分,那么代码高尔夫比代码挑战更合适。
丹尼斯

线性时间意味着它必须是O(m + n),而不是O(mxn)或O(mx(nm)),其中m,n是第一和第二个字符串的长度?
一些用户

@someuser是的,它表示O(m + n)。

1
@BetaDecay请参阅“给出两个字符串X和Y,且X的长度不超过Y,任务是确定是否存在Y的子字符串,该子字符串与X成为同构。”

Answers:


8

Python 2,338 326 323 321 310 306 297 293 290 289 280 279 266 264 259 237 230 229 226 223 222 220 219 217(260 238 231 228 225 223 223 221 220 218的退出状态为0)

exec'''s=raw_input()
S=[M-s.rfind(c,0,M)for M,c in enumerate(s)]
k=0
j=x=%s
while k<=M+x:
 if S[k]>j<W[j]or S[k]==W[j]:
    k+=1;j+=1;T+=[j]
    if j-L>x:print s[k-j:k];z
 else:j=T[j]
'''*2%('-1;T=[0];W=S;L=M',0)
print'No!'

该算法是KMP的一种变体,它使用基于索引的字符匹配测试。基本思想是,如果我们在位置上出现不匹配,X[i]则可以根据最长的后缀与X[:i]前缀同构,返回到下一个可能的匹配位置X

从左到右,我们为每个字符分配一个索引,该索引等于到该字符最近一次出现的距离,或者,如果没有先前出现的距离,则采用当前字符串前缀的长度。例如:

MISSISSIPPI
12313213913

为了测试两个字符是否匹配,我们比较索引,并针对大于当前(子)字符串长度的索引进行适当调整。

KMP算法变得有些简化,因为我们不能在第一个字符上出现不匹配的情况。

如果存在,则该程序输出第一个匹配项。在发生匹配时,我使用运行时错误退出,但是可以轻松修改代码以干净地退出,但要花一些字节。

注意:对于计算索引,我们可以使用str.rfind(与我之前使用字典的方法相反),并且仍然具有线性复杂度,假设它str.rfind从头开始搜索(这似乎是唯一明智的实现选择)-对于字母表中的每个字符,我们不必再遍历字符串的相同部分两次,因此比较的上限是(字母大小)*(字符串大小)。

由于该代码在打高尔夫球的过程中变得相当模糊,因此这是一种较早的解决方案(293字节),更易于阅读:

e=lambda a:a>i<W[i]or a==W[i]
exec('s=raw_input();S=[];p={};M=i=0\nfor c in s:S+=[M-p.get(c,-1)];p[c]=M;M+=1\nW=S;L=M;'*2)[:-9]
T=[0]*L
k=1
while~k+L:
 if e(W[k]):i+=1;k+=1;T[k]=i
 else:i=T[i]
m=i=0
while m+i<M:
 if e(S[m+i]):
    if~-L==i:print s[m:m+L];z
    i+=1
 else:m+=i-T[i];i=T[i]
print'No!'

e功能测试字符的等效性。该exec语句分配索引并进行一些变量初始化。第一个循环处理X回退值,第二个循环进行字符串搜索。

更新:这是一个可以干净退出的版本,但花费了一个字节:

r='No!'
exec'''s=raw_input()
S=[M-s.rfind(c,0,M)for M,c in enumerate(s)]
k=0
j=x=%s
while k<=M+x:
 if S[k]>j<W[j]or S[k]==W[j]:
    k+=1;j+=1;T+=[j]
    if j-L>x:r=k=s[k-j:k]
 else:j=T[j]
'''*2%('-1;T=[0];W=S;L=M',0)
print r

我认为您可以通过执行python3保存一个字节。r=raw_inputvs. r = input节省了4个字节,印刷的原

@Maltysen感谢您的评论。写这个的时候我并没有考虑Python 3中,但出于成本和储蓄,还有额外的2字节的成本,因为你不能缩进在Python 3混合空格和制表符
米奇施瓦茨

@Maltysen只是跟进,现在的问题不再是选项卡,而是括号exec
米奇·施瓦兹

这是一个非常好的答案!我希望现在能在cjam中看到它:)

6

Python 3,401个字节

import string,itertools
X=input()
Y=input()
x=len(X)
t=[-1]+[0]*~-x
j=2
c=0
while j<x:
 if X[j-1]==X[c]:c+=1;t[j]=c;j+=1
 elif c>0:c=t[c]
 else:t[j]=0;j+=1
s=string.ascii_letters
*b,=map(s.find,X)
for p in itertools.permutations(s):
 m=i=0
 while m+i<len(Y):
  if p[b[i]]==Y[m+i]:
   if~-x==i:print(Y[m:m+x]);exit()
   else:i+=1
  else:
   if-1<t[i]:m+=i-t[i];i=t[i]
   else:i=0;m+=1
else:print("No!")

这仍然是大多数问题,但是我认为它应该起作用。核心算法是KMP,外加一个附加因数,该因数是字母大小的因数(这很好,因为字母是恒定的)。换句话说,这是/应该是一种完全不切实际的线性算法。

以下是一些有助于分析的注释:

# KMP failure table for the substring, O(n)
t=[-1]+[0]*~-x
j=2
c=0
while j<x:
 if X[j-1]==X[c]:c+=1;t[j]=c;j+=1
 elif c>0:c=t[c]
 else:t[j]=0;j+=1

# Convert each char to its index in a-zA-Z, O(alphabet * n)
s=string.ascii_letters
*b,=map(s.find,X)

# For every permutation of letters..., O(alphabet!)
for p in itertools.permutations(s):
 # Run KMP, O(n)
 m=i=0
 while m+i<len(Y):
  if p[b[i]]==Y[m+i]:
   if~-x==i:print(Y[m:m+x]);exit()
   else:i+=1
  else:
   if-1<t[i]:m+=i-t[i];i=t[i]
   else:i=0;m+=1
else:print("No!")

为了进行测试,您可以s使用比小的字母替换string.ascii_letters


2

APL(Dyalog),32字节

这是一个中缀函数,将X作为左参数,将Y作为右参数。

{(s,⊂'No!')⊃⍨(⍳⍨¨s←⍵,/⍨≢⍺)⍳⊂⍳⍨⍺}

在线尝试!

{} 匿名lambda其中代表参数(X和Y)

⍳⍨⍺ɩ X的ndex自拍(ɩ在X X的元素的第一次出现的ndices)

 附上,以便我们可以寻找整个模式

(… that第一次出现的)⍳ndex

  ≢⍺ X的计数(长度)

  ⍵,/⍨ Y大小的所有子字符串(减少这些字符串的串联,但这是一个空操作)

  s← 存储在s(用于s ubstrings)

  ⍳⍨¨ɩ每个人的ndex自拍照

 现在我们有了第一个模式的索引,如果找不到匹配项,则为1 +模式数

()⊃⍨ 使用该索引从…中进行选择

  ⊂'No!' 包含的字符串(以便它作为单个元素起作用)

  s, 前置 s

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.