本地字符串时间段


20

当地时期

接受一个非空字符串s。所述本地周期小号在索引是最小的正整数Ñ,对于每个这样的0≤ķ<N ,我们有S [I + K] = S [I-N + K]每当两侧限定。可替代地,它是一个非空字符串的最小长度瓦特使得如果级联WW被放在旁边小号使得的第二拷贝瓦特开始于索引小号,那么这两个字符串同意任何地方重叠。

例如,让我们计算(基于0的)索引2 的s =“ abaabbab”的本地周期。

  • 尝试n = 1:然后s [2 + 0]≠s [2-1 + 0],因此此选择不正确。
  • 尝试n = 2:然后s [2 + 0] = s [2-2 + 0]s [2 + 1]≠s [2-2 + 1],所以这也不正确。
  • 尝试n = 3:然后未定义s [2 + 0-3]s [2 + 1] = s [2-3 + 1]s [2 + 2] = s [2-3 + 2]。因此,本地期间为3。

这是使用第二个定义的本地时间段的可视化,为了清楚起见,在w的两个副本之间添加了分号:

index      a b a a b b a b      period
 0       a;a                     1
 1       b a;b a                 2
 2       a a b;a a b             3
 3             a;a               1
 4     b b a b a a;b b a b a a   6
 5                 b;b           1
 6               a b b;a b b     3
 7                   b a;b a     2

请注意,w不一定是s的子字符串。这是在index-4情况下发生的。

任务

您的输入是小写ASCII字符的非空字符串s。如果需要,可以将其视为字符列表。您的输出应为在每个索引处包含s的本地周期的列表。在上面的示例中,正确的输出应为[1,2,3,1,6,1,3,2]

每种语言的最低字节数为准。标准适用规则。

测试用例

a -> [1]
hi -> [1, 2]
www -> [1, 1, 1]
xcxccxc -> [1, 2, 2, 5, 1, 3, 2]
abcbacb -> [1, 4, 7, 7, 7, 3, 3]
nininini -> [1, 2, 2, 2, 2, 2, 2, 2]
abaabbab -> [1, 2, 3, 1, 6, 1, 3, 2]
woppwoppw -> [1, 4, 4, 1, 4, 4, 4, 1, 4]
qwertyuiop -> [1, 10, 10, 10, 10, 10, 10, 10, 10, 10]
deededeededede -> [1, 3, 1, 5, 2, 2, 5, 1, 12, 2, 2, 2, 2, 2]
abababcabababcababcabababcaba -> [1, 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 19, 19, 5, 5, 2, 5, 5, 12, 12, 2, 2, 2, 7, 7, 5, 5, 2]

@Arnauld总是可以找到与s长度相同的w。在的情况下,w将是的旋转版本。另请参见索引4的示例:w不一定是s的子字符串。qwertyuiopqwertyuiop
Zgarb

那讲得通。我看错了挑战。
Arnauld

线性时间解决方案的虚构奖励!(其他人可能会提供真正的赏金,所以请继续尝试)
user202729 '18

确实很巧妙,但是我想知道定义两个字符之间每个位置的局部时间段是否更有意义(即;示例中的位置)。这将摆脱领先1
马丁安德

@MartinEnder从概念上讲更干净,但是此定义使通过遍历字符串更容易产生输出,并且输出不会为空。
Zgarb

Answers:


4

视网膜89 86字节

.
$`¶$<'¶
/(^|.+)¶.+/_(Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4
G`.
%C`.
N`
0G`

在线尝试!编辑:由于@MartinEnder,节省了3个字节。说明:

.
$`¶$<'¶

在每个字符处分割输入,创建一对线,一个用于前缀,一个用于前缀后缀。

/(^|.+)¶.+/_(

在每个结果对上运行脚本的其余部分。

Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4

查找所有重叠的匹配项并列出结果。(见下文。)

G`.

丢弃空匹配项。

%C`.

取每场比赛的时间。

N`

按数字排序。

0G`

取最小的。

匹配通过将前缀和后缀分为三部分来进行。有四种有效的情况需要考虑:

AB|BC   B matches B to the left and B to the right
B|ABC   AB matches [A]B to the left and AB to the right
ABC|B   BC matches BC to the left and B[C] to the right
BC|AB   ABC matches [A]BC to the left and AB[C] to the right

因此,正则表达式仅允许A和C一次在一侧匹配。


$&$'等于,$<'并且计算线长较短%C`.tio.run/##K0otycxLNPz/X49LJeHQNhUb9UPbuPQ14mr0tDUPbdPT1o/...
马丁安德

4

爪哇8,167个 154 152字节

s->{int l=s.length,r[]=new int[l],i=0,n,k;for(;i<l;r[i++]=n)n:for(n=0;;){for(k=++n;k-->0;)if(i+k<l&i+k>=n&&s[i+k]!=s[i-n+k])continue n;break;}return r;}

-2个字节,感谢@ceilingcat

在线尝试。

说明:

s->{                          // Method with char-array parameter and int-array return-type
  int l=s.length,             //  Length of the input-array
      r[]=new int[l],         //  Result-array of the same size 
      i=0,n,k;                //  Integers `i`, `n`, and `k` as defined in the challenge
  for(;i<l;                   //  Loop `i` in the range [0, `l`):
      r[i++]=n)               //    After every iteration: Add `n` to the array
    n:for(n=0;;){             //   Inner loop `n` from 0 upwards indefinitely
      for(k=++n;k-->0;)       //    Inner loop `k` in the range [`n`, 0]:
                              //    (by first increasing `n` by 1 with `++n`)
        if(i+k<l&i+k>=n)      //     If `i+k` and `i-n+k` are both within bounds,
           &&s[i+k]!=s[i-n+k])//     and if `s[i+k]` is not equal to `s[i-n+k]`:
          continue n;         //      Continue loop `n`
                              //    If we haven't encountered the `continue n` in loop `k`:
      break;}                 //     Break loop `n`
  return r;}                  //  Return the result

1

JavaScript(ES6),84个字节

将输入作为字符数组。

s=>s.map((_,i)=>s.some(_=>s.every(_=>k<j|!s[k]|s[k-j]==s[k++]|k-i>j,++j,k=i),j=0)*j)

测试用例


我不确定是否允许使用字符数组,您确定它们不只是1个字符的字符串吗?
暴民埃里克(Erik the Outgolfer)'18年

@EriktheOutgolfer JS中没有字符类型,所以是的:从技术上讲,它是一个由1个字符组成的字符串数组。我的理解是,如果它像字符串一样嘎嘎响,那就是字符串。(这是一篇有关此问题的元文章,但可能存在更相关的文章,或者实际上与我的假设相矛盾。)
Arnauld

1
或换句话说:这与我们在JS中列出的字符列表非常接近,这是OP明确允许的。
Arnauld

1

红宝石104102字节

->s{l=s.size-1
(0..l).map{|i|n=0
loop{n+=1
(n-i..l-i).all?{|k|k<0||k>=n||s[i+k]==s[i-n+k]}&&break}
n}}

在线尝试!

Lambda接受一个字符串并返回一个数组。

-2个字节:具有索引绑定防护的交换范围端点

取消高尔夫:

->s{
  l=s.size-1                # l is the maximum valid index into s
  (0..l).map{ |i|           # i is the current index
    n=0                     # n is the period being tested
    loop{                   # Repeat forever:
      n+=1                  # Increment n
      (n-i..l-i).all?{ |k|  # If for all k where i+k and i-n+k are valid indexes into s
        k<0 || k>=n ||      #   We need not consider k OR
          s[i+k]==s[i-n+k]  #   The characters at the relevant indexes match
      } && break            # Then stop repeating
    }
  n                         # Map this index i to the first valid n
  }
}

1

Japt33 32字节

@Shaggy节省了1个字节

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ

在线测试!

说明

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ   Implicit: U = input string
¬Ë                                 Split the input into chars, and map each index E to
  @                          }aÄ     the smallest positive integer D where
   ¯E                                  the first E chars of U
      f                                matches the regex formed by
          UtED                         taking D chars of U from index E,
                ú.D                     padding to length D with periods,
                    r."($&|^)"          replacing each char C with "(C|^)",
        '$i                             and placing a '$' at the very end.

我的第一个念头就是将左子字符串中的每个字符与右子字符串中的对应字符进行比较,就像JS答案中那样。但是,这是行不通的,因为如果索引为负数或太大,Japt的获取字符的方法将自动换行到字符串的另一端。

相反,我的解决方案从第二个子字符串中构建了一个正则表达式,并在第一个子字符串上对其进行了测试。让我们以测试用例中的第五项abaabbab为例:

abaabbab
    ^ split point -> abaa for testing regex, bbab for making regex

   slice  regex                              matches abaa
1. b      /(b|^)$/                           no
2. bb     /(b|^)(b|^)$/                      no
3. bba    /(b|^)(b|^)(a|^)$/                 no
4. bbab   /(b|^)(b|^)(a|^)(b|^)$/            no
5. bbab.  /(b|^)(b|^)(a|^)(b|^)(.|^)$/       no
6. bbab.. /(b|^)(b|^)(a|^)(b|^)(.|^)(.|^)$/  yes: /^^ab..$/

主要技巧是 ^可以无限匹配,直到匹配实际字符为止。这使我们可以忽略从正则表达式开始的任何数量的字符,同时确保其余所有字符都连续匹配,并在测试字符串的末尾完成。

我不确定我的解释是否很好,所以请让我知道您是否需要澄清或其他需要解释的内容。



@Shaggy谢谢,那个分号让我
烦恼

1

C(GCC) 143个 142 140 139 128 126 123字节

  • 保存一个字节。Golfed !b&&printfb||printf
  • 感谢Kevin Cruijssen保存了两个字节。for通过处理printf放置位置来删除循环主体括号。
  • 保存一个字节。Golfed b+=S[i+k]!=S[i-n+k]b|=S[i+k]-S[i-n+k]
  • 保存了十一个字节。无需l=strlen(S)通过调节两个字符串处理循环来达到到达字符串末尾(空字节'\0')的需要。
  • 保存了两个字节。Golfed i-n+k>~0i-n>~k
  • 多亏了ceilingcat,节省了三个字节;b||printf("|"),n++等同于n+=b||printf("|")
i,b,k,n;f(char*S){for(i=~0;S[++i];)for(b=n=1;b;n+=b||printf("%d,",n))for(b=k=0;k<n&&S[i+k];k++)b|=n-i>k?0:S[i+k]-S[i-n+k];}

在线尝试!



@KevinCruijssen谢谢。
乔纳森·弗雷希

@ceilingcat谢谢;整齐的等效性,那个。
乔纳森·弗雷希

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.