键入给定文本所需的击键次数最少


45

我们都知道程序员往往很懒。为了最大限度地利用您的空闲时间,您决定编写一个程序,该程序为输入的文本输出最少的击键次数。

输入:必须转换为击键的文本。您可以决定如何输入文本(STDIN /从参数中提供的文件中读取)

输出:以下格式的必要操作:

  • 他们必须编号
  • H它:按下一个键并立即释放它
  • P提示:按下一个键然后不松开(将键R作为下一个击键时,这将永远不是最佳选择)
  • Release:释放P密钥

范例

输入:

Hello!

输出:

一个幼稚的解决方案是:

1 P Shift
2 H h
3 R Shift
4 H e
5 H l
6 H l
7 H o
8 P Shift
9 H 1
10 R Shift

这样会更有效:

1 P Shift
2 H h
3 H 1
4 R Shift
5 H Left
6 H e
7 H l
8 H l
9 H o

环境:

  • 编辑器使用等宽字体
  • 文本采用80个字符的软包装
  • 向上箭头和向下箭头保留该列,即使它们之间的行较短
  • 剪贴板被认为是空的
  • 假定启用了数字锁定
  • 大写锁定被假定为禁用
  • 大写锁定仅适用于字母(即无Shift锁定)

热键/快捷方式

  • Home:跳到当前行的开头
  • End:跳到当前行的末尾
  • Ctrl+ A:标记所有内容
  • Ctrl+ C:复制
  • Ctrl+ X:切
  • Ctrl+ V:粘贴
  • Shift+光标移动:标记
  • Ctrl+ F:打开搜索对话框。
    • 愚蠢的文字匹配,没有正则表达式
    • 区分大小写
    • 搜索环绕
    • 用于搜索的单行文本输入
    • 输入将被当前选择预填充,除非两者之间有换行符,否则将选择完整的输入
    • 复制/粘贴照常进行
    • 按下Enter执行搜索,选择当前光标位置之后的第一个匹配项
  • F3:重复上一次搜索
  • Ctrl+ H:打开替换对话框
    • 愚蠢的文字匹配,没有正则表达式
    • 区分大小写
    • 全部替换,环绕
    • 单行文字输入
    • 搜索输入将预先填充当前选择,除非两者之间有换行符,否则将选择完整输入
    • 替换输入为空
    • 复制/粘贴照常进行
    • Tab 跳转到替换输入
    • 按下将Enter执行全部替换。光标放置在最后一次替换之后

规则

  • 解决方案必须是一个完整的程序,可以在不进行任何进一步修改的情况下进行编译/解析和执行
  • 上面显示的键盘是要使用的键盘
    • 不需要处理无法用它键入的字符
  • 每个密钥必须在最后释放
  • 游标不必位于文件末尾

得分

您的分数是键入以下文本所需的操作总数之和。获胜者是得分最低的解决方案。使用天真的解决方案,我得到了1371 + 833 + 2006 = 4210。打败它!我将在两周内选出一名优胜者。

1我天真的解决方案

number = 1

H = (char) -> console.log "#{number++} H #{char}"
P = (char) -> console.log "#{number++} P #{char}"
R = (char) -> console.log "#{number++} R #{char}"

strokes = (text) ->
    shiftActive = no

    for char in text
        if /^[a-z]$/.test char
            if shiftActive
                R "Shift"
                shiftActive = no

            H char
        else if /^[A-Z]$/.test char
            unless shiftActive
                P "Shift"
                shiftActive = yes

            H char.toLowerCase()
        else
            table =
                '~': '`'
                '!': 1
                '@': 2
                '#': 3
                '$': 4
                '%': 5
                '^': 6
                '&': 7
                '*': 8
                '(': 9
                ')': 0
                '_': '-'
                '+': '='
                '|': '\\'
                '<': ','
                '>': '.'
                '?': '/'
                ':': ';'
                '"': "'"
                '{': '['
                '}': ']'

            if table[char]?
                unless shiftActive
                    P "Shift"
                    shiftActive = yes

                H table[char]
            else
                H switch char
                    when " " then "Space"
                    when "\n" then "Enter"
                    when "\t" then "Tab"
                    else
                        if shiftActive
                            R "Shift"
                            shiftActive = no

                        char
    R "Shift" if shiftActive

input = ""

process.stdin.on 'data', (chunk) -> input += chunk
process.stdin.on 'end', -> strokes input

2容易重复

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

3更复杂的重复

We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it
And if you ask me how I'm feeling
Don't tell me you're too blind to see

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

(Ooh, give you up)
(Ooh, give you up)
(Ooh)
Never gonna give, never gonna give
(Give you up)
(Ooh)
Never gonna give, never gonna give
(Give you up)

We've know each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it

I just wanna tell you how I'm feeling
Gotta make you understand

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

您可以使用我编写的重播程序来测试您的解决方案(注意:它尚不支持“搜索/替换”,其他所有功能都可以使用)。


6
我很乐意看到这样的vim程序。
Braden Best

4
通常,我会使用鼠标来完成部分操作。
Victor Stafusa 2014年

1
很有意思。我早上去; 3
cjfaure 2014年

2
您真的不需要Rick Roll我们,对吗?:)
Filip Haglund 2014年

1
我有点@ B1KMusic。对我来说,生成vimgolf的解决方案会更有趣。(这等效于您仅使用vim命令在此处尝试做的事情。)但这听起来很有趣,但是减少键击非常困难(或者至少我认为是这样),因为很难精确地进行选择。这使得复制和粘贴是一项非常艰巨的任务,并且几乎要进行与您尝试复制的事物相同的击键。(或者至少这是我在阅读复制和粘贴的方式。)而且我看不到其他许多减少按键的方法。
FDinoff 2014年

Answers:


11

Haskell 1309 + 457 + 1618 = 3384

最后,是一个答案(一旦我意识到您的第一个测试中有选项卡就可以编辑问题以查看那些答案,分数就大大提高了)。用编译ghc,在stdin上提供输入。例:

$ ghc keyboard.hs && echo hello|./keyboard
1 H h
2 H e
3 H l
4 H l
5 H o
6 H Enter

我尝试了类似Dijkstra这样的显而易见的方法,但即使将分支缩减为唯一有用的动作,它的速度仍然太慢了:输出下一个键,或者从行的开头进行复制(Shift + Home,Ctrl + C,结束),或粘贴。

因此,此方法使用固定长度的剪贴板,在行前缀即将变得“有用”时进行复制,并继续使用该前缀,只要它可以粘贴到比下一行到达的行前缀更多的行上即可。当它不能使用剪贴板时,它会退回到朴素的解决方案上,因此,一旦选择的长度超过副本成本,就可以保证将其击败。

当选择前缀长度以适合“ Never gonna”时,将获得最低分数。有很多方法可以对此进行改进,但是我已经阅读Rick Astley足够了。

import Data.List (isPrefixOf,isInfixOf)
import Control.Monad (foldM)
plen=12
softlines text=sl 0 [] text
  where
    sl n [] [] = []
    sl n acc [] = [(n,reverse acc)]
    sl n acc (x:xs)
      |x=='\n'||length acc==79=(n,reverse (x:acc)):(sl (n+1) [] xs)
      |otherwise=sl n (x:acc) xs
pasteable (a,b) (c,d)=(c>a && b`isInfixOf`d)
                      || (c==a && b`isInfixOf`(drop (length b) d))
findprefixes l=filter (\(a,b,c)->c/=[])
               $ map (\(a,b)->(a, b, map fst $ filter (pasteable (a,b)) l))
               $ filter (\(a,b)->length b==plen && last b/='\n')
               $ map (\(a,b)->(a, take plen b)) l
mergePrefixes [] = []
mergePrefixes (p:ps) = mergePrefixes' p ps
 where mergePrefixes' p [] = [p]
       mergePrefixes' (a,x,b) ((c,y,d):qs) =
         if length (filter (>=c) b) >= length d then
           mergePrefixes' (a,x,b) qs
         else
           (a, x, (filter (<c) b)):(mergePrefixes' (c,y,d) qs)
uc = ("~!@#$%^&*()_+<>?:{}|\""++['A'..'Z'])
lc = ("`1234567890-=,./;[]\\'"++['a'..'z'])
down c = case [[lo]|(lo,hi)<-zip lc uc,c==hi] of []->error [c];p->head p
applyPrefixToLine prefix [] s=return s
applyPrefixToLine [] line s=emit line s
applyPrefixToLine prefix line@(ch:rest) s=
 if prefix`isPrefixOf`line then
   do { s<-emitPaste s; applyPrefixToLine prefix (drop (length prefix) line) s}
 else
   do { s<-emitch s ch; applyPrefixToLine prefix rest s}
type Keystroke = (Char, [Char])
key action k (n, shift) = do
  putStrLn ((show n)++" "++[action]++" "++k)
  if k=="Shift" then return (n+1, (not shift))
  else return (n+1, shift)
emitch (m, shift) ch=
  case ch of
    '\t'->key 'H' "Tab" (m,shift)
    '\n'->key 'H' "Enter" (m,shift)
    ' '->key 'H' "Space" (m,shift)
    _->
      if shift && ch`elem`lc then
        do { key 'R' "Shift" (m, True); key 'H' [ch] (m+1, False) }
      else if not shift && ch`elem`uc then
             do { key 'P' "Shift" (m, False); key 'H' (down ch) (m+1, True) }
           else if ch`elem`lc
                then key 'H' [ch] (m, shift)
                else key 'H' (down ch) (m, shift)
emit line s = foldM emitch s line
emitPaste s = do
  s<-key 'P'"Ctrl" s
  s<-key 'H' "v" s
  key 'R' "Ctrl" s
emitCopy s = do
  s<-key 'H' "Home" s
  s<-key 'P'"Ctrl" s
  s<-key 'H' "c" s
  s<-key 'R' "Ctrl" s
  s<-key 'R' "Shift" s
  key 'H' "End" s
applyPrefix pf ((a,b):xs) p@((c,y,d):ps) s=
  if (c==a) then
    do
      s@(n, shift) <- emit y s
      s <- if shift then return s else key 'P' "Shift" s
      s <- emitCopy s
      s <- applyPrefixToLine y (drop (length y) b) s
      applyPrefix y xs ps s
  else
    do
      s<-applyPrefixToLine pf b s
      applyPrefix pf xs p s
applyPrefix "" ((a,b):xs) [] s=
  do
    s <- emit b s
    applyPrefix "" xs [] s
applyPrefix pf ((a,b):xs) [] s=
  do
    s<-applyPrefixToLine pf b s
    applyPrefix pf xs [] s
applyPrefix _ [] _ s=return s

main=do
  input <- getContents
  let lines = softlines input
  let prefixes = mergePrefixes (findprefixes lines)
  (n,shift) <- applyPrefix "" lines prefixes (1, False)
  if shift then
    key 'R' "Shift" (n, shift)
  else
    return(n,shift)

很好的解决方法:)顺便说一句:您可以通过组合粘贴(如果可能)来去除更多的字符。
TimWolla 2014年

这只会真正影响示例2-我有一个Dijkstra算法版本可以找到该示例,但是它仅可用于前3行。您可以通过尝试使用不同的前缀大小来改进所有测试的解决方案;该解决方案的速度足够快,您可以通过蛮力执行此操作,只需运行大约10次。很难在haskell中重构它。
bazzargh 2014年
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.