内翻弦


21

平衡字符串是一串括号,()因此每个括号都可以与另一个匹配。更严格地说,它们是该语法所涵盖的字符串:

S → (S)S | ε

我们可以通过以下方式将字符串“由内而外”:

  • 开关的所有事件(,并)互相

  • 将字符从字符串的前面移到后面,直到再次平衡为止。


让我们举个例子。

我们从平衡的字符串开始:

(()(())())

然后我们切换括号

))())(()((

然后将字符从字符串的开头移到字符串的后面,直到字符串平衡为止。

))())(()((
)())(()(()
())(()(())
))(()(())(
)(()(())()
(()(())())

那就是我们的结果!


请注意,有些字符串可以用多种方式内翻,例如字符串

(()())

从里到外可以是:

()(())

要么

(())()

但是,每个字符串至少都有一个解决方案

任务

编写一个程序,以平衡的字符串作为输入并输出,该字符串由内而外。如果可能有多个有效输出,则只需要输出其中之一即可。您可以使用不同的支柱型(<>[]{}),如果你愿意的话。

这是一场竞赛,因此您应力求最大程度地减少以字节为单位的源代码大小。

测试用例

(()())     -> ()(()), (())()
(()(())()) -> (()(())())
((())())() -> (()(()()))

是否保证始终有解决方案?
路易斯·门多

@LuisMendo是的,我已经证明了这一点。如果您想查看证明,请随时与我聊天。
小麦巫师

谢谢。我知道这一点就足够了。也许您应该将它写成挑战,否则,如果没有解决方案,您将需要定义输出内容
Luis Mendo

Answers:


9

Haskell中124 120 119 117 113 110 109 106 105 104 101 98字节

由于bartavelle节省了4个字节!

Zgarb节省了3个字节

感谢Peter Taylor节省了1个字节

这是我在Haskell中制定的解决方案。它的好,现在很不错感谢一些帮助,我收到,但我正在寻找使这个更短,所以反馈/建议表示赞赏。

until(!0)g.map d
_!1=1<0
('(':a)!x=a!(x-1)
(_:a)!x=a!(x+1)
_!_=1>0
g(a:b)=b++[a]
d '('=')'
d _='('

在线尝试!

说明

该程序定义了4个函数,第一个函数(!)确定字符串是否平衡。其定义如下:

_!1=1<0
('(':a)!x=a!(x-1)
(_:a)!x=a!(x+1)
_!_=1>0

由于彼得·泰勒(Peter Taylor)的建议,此检查假定输入的打开和关闭位数相等。

下一个g将旋转字符串一次。

g(a:b)=b++[a]

然后,我们d只需简单地将其镜像

d '('=')'
d _='('

最后,我们有了我们要关注的功能。在此,我们使用带有和until(!0)g组成的无点表示形式map d,该表示形式映射d到输入并应用g到结果达到平衡为止。这是问题中描述的确切过程。

until(!0)g.map d

1
您可以使用g x@(a:b)|x!0=x|1>0=g$b++[a]删除一些字节,并删除的括号d '('=')'
bartavelle

@bartavelle删除的d原因会导致编译器错误,相信我我已经尝试过。但是第一个建议是值得欢迎的。谢谢!
小麦巫师

1
您可以保存另一个字节,!因为您不需要处理字符串的开括号和闭括号的数目不相等的情况,因此您可以交换前两种情况并具有_!1=1<0 []!_=0<1
Peter Taylor

1
until缩短使用时间gTIO
Zgarb

2
我认为应该通过d映射'('(-1)以及其他任何内容来节省大量的钱1,然后!可以将的两个最长的情况合并为(i:a)!x=a!(x+i)。然后,顶层结构需要重新构造以map d适应这种until情况,我必须运行,所以我现在没有时间弄清楚需要使用哪些组合器将它们粘合在一起。
彼得·泰勒

7

SOGL V0.1212 11 个字节

↔]»:l{Ƨ()øŗ

在这里尝试!

说明:

↔            mirror characters
 ]           do ... while the top of stack is truthy
  »            put the last letter at the start
   :           duplicate it
    l{         length times do
      Ƨ()        push "()"
         ø       push ""
          ŗ      replace ["()" with ""]
             if the string left on stack is empty (aka all matched parentheses could be removed), then stop the while loop

注意:l{可以替换为 10字节,但是很遗憾,它没有实现。


您确定镜像角色有效吗?我不确切知道这是什么意思,但是我的直觉告诉我,它也颠倒了字符的顺序,我认为这是行不通的。
小麦巫师

1
@Olmman它旨在扭转字符,但不(其在这里节省了字节!)。在V0.13s阵容中可以更改波谷。示例
dzaima

5

果酱(20个字符)

q1f^0X${~_}%_:e>#)m<

在线演示

或相同的字符数

q1f^_,,{0W$@<~}$W=m<

在线演示

解剖

这两个版本具有相同的页眉和页脚

q1f^    e# Read input and toggle least significant bit of each character
        e# This effectively swaps ( and )

m<      e# Stack: swapped_string index
        e# Rotates the string to the left index characters

然后,中间的钻头显然会计算出旋转所需的距离。他们两个都使用评估,并且依赖于(作为CJam减量运算符和)作为增量运算符。

0X$     e# Push 0 and a copy of the swapped string
{~_}%   e# Map: evaluate one character and duplicate top of stack
        e# The result is an array of the negated nesting depth after each character
_:e>    e# Copy that array and find its maximum value
#       e# Find the first index at which that value occurs
)       e# Increment

_,,     e# Create array [0 1 ... len(swapped_string)-1]
{       e# Sort with mapping function:
  0W$@  e#   Rearrange stack to 0 swapped_string index
  <~    e#   Take first index chars of swapped_string and evaluate
}$      e# The result is an array of indices sorted by the negated nesting depth
W=      e# Take the last one

3

的JavaScript(ES6),111个 105字节

(通过@CraigAyre保存了2个字节,通过@PeterTaylor保存了2个字节,感谢@Shaggy保存了2个字节。)

s=>(r=[...s].map(c=>'()'[c<')'|0])).some(_=>r.push(r.shift(i=0))&&!r.some(c=>(i+=c<')'||-1)<0))&&r.join``

取消高尔夫:

s=>(
  r=[...s].map(c=>'()'[c<')'|0]),  //switch "(" and ")"
  r.some(_=>(
    r.push(r.shift(i=0)),          //move last element to beginning of array, initialize i
    !r.some(c=>(i+=c<')'||-1)<0)   //check if balanced (i should never be less than 0)
  )),
  r.join``
)

测试用例:


3

视网膜46 38字节

T`()`)(
(.*?)(((\()|(?<-4>\)))+)$
$2$1

在线尝试!链接包括测试用例。编辑:在@MartinEnder的帮助下保存了8个字节。第一阶段简单地转置括号,而第二阶段寻找最长后缀,即有效的平衡前缀,这显然是旋转完全平衡的充分条件。使用平衡组检测平衡。只要我们已经()看到了许多s,该构造就可以((\()|(?<-4>\)))+匹配任意数量的(s和任意数量的s。由于我们只是在寻找有效的前缀,因此我们不必匹配其余的。)<-4>()


通常,您不必重复两个括号,而是将它们交替放置,这样可以节省一个byte ((\()|(?<-2>\)))。但是您的尝试激发了我找到一种全新的方法,该方法可以节省另外两个:(?<-1>(\()*\))+。将来肯定会派上用场,所以谢谢。:)
Martin Ender

它甚至更短的第一后缀,你可以通过它到达字符串的结尾没有得到一个负的堆深度匹配来确定旋转:tio.run/...
马丁安德

@MartinEnder我本来尝试过轮换,但当时无法正常工作,但我(?<-1>(\()*\))+什至看不到它如何工作,因为它似乎想1在实际匹配任何东西之前就从堆栈中弹出……
Neil

@MartinEnder碰巧的是,在匹配平衡前缀时,替代版本似乎是高尔夫球手。
尼尔

1
实际弹出发生在组的末尾,而不是开始。交替进行的好处是避免重复\(*
Martin Ender

2

PHP,110 108个字节

for($s=$argn;;$p?die(strtr($s,"()",")(")):$s=substr($s,1).$s[$i=0])for($p=1;$p&&$c=$s[$i++];)$p-=$c<")"?:-1;

作为管道运行-nR在线测试

分解

for($s=$argn;               # import input
    ;                       # infinite loop
    $p?die(strtr($s,"()",")(")) # 2. if balanced: invert, print and exit
    :$s=substr($s,1).$s[$i=0]   #    else: rotate string, reset $i to 0
)                               # 1. test balance:
    for($p=1;                   # init $p to 1
        $p&&$c=$s[$i++];)       # loop through string while $p is >0
        $p-=$c<")"?:-1;             # increment $p for ")", decrement else


2

八度,62字节

@(s)")("(x=hankel(s,shift(s,1))-39)(all(cumsum(2*x'-3)>=0)',:)

在线尝试!

该函数将字符串作为输入并输出所有结果。

说明:

           hankel(a,shift(a,1))                                % generate a matrix of n*n where n= length(s) and its rows contain incresing circulraly shifted s
         x=...                 -39                             % convert matrix of "(" and ")" to a mtrix of 1 and 2
    ")("(x                        )                            % switch the parens
                                               2*x'-3          % convert [1 2] to [-1 1]
                                        cumsum(      )         % cumulative sum along the rows
                                    all(              >=0)'    % if all >=0
                                   (                       ,:) % extract the desired rows

2

Mathematica,78个字节

""<>{"(",")"}[[2ToCharacterCode@#-81//.x_/;Min@Accumulate@x<0:>RotateLeft@x]]&

1

JavaScript(ES6),97个字节

f=(s,t=s,u=t.replace(')(',''))=>u?t==u?f(s.slice(1)+s[0]):f(s,u):s.replace(/./g,c=>c<')'?')':'(')

通过递归旋转输入字符串直到其转置平衡,然后进行转置来工作。


简单的美丽。
里克·希区柯克

1

APL(Dyalog Unicode)35 30字节

@Adám为新方法打下了基础

1⌽⍣{2::01∊⍎⍕1,¨⍺}')('['()'⍳⎕]

在线尝试!

打高尔夫球正在进行中。

说明

'()'⍳⎕              Find the index of each character of the input in the string '()'
                    (this is 1-indexed, so an input of '(())()' would give 1 1 2 2 1 2)
')('[...]           Find the index of the vector in the string ')('
                    This essentially swaps ')'s with '('s and vice versa
                   On this new string, do:
 1                   rotate it one to the left
                    Until this results in 1:
 1,¨⍺                 Concatenate each element of the argument with a 1
                      This inserts 1 one before each parenthesis
                     Stringify it
                     And evaluate it, if the parentheses are balanced, this produces no errors
 1                   Check if 1 belongs to evaluated value
                      If the parentheses were not matches during ⍎, this causes a syntax error
 2::0                 This catches a syntax error and returns 0
                      Essentially this code checks if the brackets are balanced or not

0

Python 2,99个字节

r=[0];S=''
for c in input():b=c>'(';r+=[r[-1]+2*b-1];S+=')('[b]
n=r.index(min(r))
print S[n:]+S[:n]

在线尝试!

以函数形式提供简单的测试用例:

Python 2,108字节

def f(s):
 r=[0];S=''
 for c in s:b=c>'(';r+=[r[-1]+2*b-1];S+=')('[b]
 n=r.index(min(r))
 return S[n:]+S[:n]

在线尝试!

这使用了一种稍有不同的方法-如果递归旋转字符串,而不是递归旋转字符串,如果我们认为括号是对某个平衡计数器进行递增和递减,则平衡的字符串绝对不能具有增量的总和-递减小于0。

所以我们采取

(()(())())

反转括号:

))())(()((

并将其转换为增量/减量之和的列表:

[-1,-2,-1,-2,-3,-2,-1,-2,-1,0]

-3是索引4处的最小值(从零开始);所以我们想按那个索引+1移动。这样可以保证累积的增减量永远不会小于0。并将总和为0。


在手机上无法测试,但是您可以r=0,代替r=[0]吗?
Cyoce

如果你正在使用@ Cyoce的建议去,你将需要更换r+=[r[-1]+2*b-1]使用r+=r[-1]+2*b-1,,以及
OVS

0

Clojure,118个字节

#(loop[s(map{\(\)\)\(}%)](let[s(conj(vec(rest s))(first s))](if(some neg?(reductions +(map{\( 1\) -1}s)))(recur s)s)))

返回一个字符序列,所以我这样称呼它:

(apply str (f "(()(())())"))
; "(()(())())"

首先翻转方括号,然后循环,直到方括号的累积总和在序列的某个点变为负数时为止。


0

brainfuck,82个字节

,[++[->->++<<]-[--->+>-<<]>-->+[-[-<<+>>>>+<<]],]+[<<]>>>[.[-]>>]<[<<]<[<<]>>[.>>]

在线尝试!

说明

读取每个字符后,将对计数器进行如下修改:

  • 计数器从0开始。
  • 每次之后),计数器加1。
  • 在每次之后(,除非计数器为0,否则计数器将减少1,在这种情况下,计数器不变。

当且仅当此计数器为0时,每个前缀才是平衡字符串的有效后缀(在反转之后)。此代码使用最长的此类前缀来形成输出。

,[                   Take input and start main loop
                     The cell one space right is the output cell (0 at this point),
                     and two spaces right is a copy of the previous counter value
  ++                 Add 2 to input
  [->->++<<]         Negate into output cell, and add twice to counter
  -[--->+>-<<]       Add 85 to output cell, and subtract 85 from counter
  >-->+              Subtract 2 from output cell and add 1 to counter
                     The output cell now has (81-input), and the counter has been increased by (2*input-80)
  [-[-<<+>>>>+<<]]   If the counter is nonzero, decrement and copy
,]
+[<<]                Go to the last position at which the counter is zero
>>>                  Go to following output character
[.[-]>>]             Output from here to end, clearing everything on the way
                     (Only the first one needs to be cleared, but this way takes fewer bytes)
<[<<]                Return to the same zero
<[<<]>>              Go to beginning of string
[.>>]                Output remaining characters
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.