20
\$\begingroup\$

Inspiration

The longest words that can be typed with only the left hand on a standard QWERTY keyboard are "sweaterdresses", "tesseradecades", and "aftercataracts" (Source: Wikipedia).

Challenge

Given as input a "keyboard" \$K\$ and a string \$S\$ determine whether \$S\$ can be typed using only the left hand on keyboard \$K\$.

Input

The keyboard \$K\$ will be provided as a list of 3 rows. You may take this input in any reasonable format (eg. a list of 3 strings, a list of 3 lists of characters, etc.), but please do not take the left-hand-side and right-hand-side of the keyboard separately, as that defeats the purpose of the challenge.

You may assume that the input contains only lowercase letters (or only uppercase letters if you wish). Each of the three rows of the keyboard may be of any non-zero size, but each letter from a-z will appear only once on the keyboard.

Example 1: [qwertyuiop, asdfghjkl, zxcvbnm]

Example 2: [qazwsxplkm, edcrfv, tgbyhnuji]

The string \$S\$ may also be taken as input in any reasonable format.

Output

Output a truthy value if the string \$S\$ can be typed using the left hand on the keyboard \$K\$ and a falsey value otherwise.

For the purposes of this challenge: A word can be typed with the left hand if it is made up solely of letters appearing in the first half of each row of the keyboard. If a row contains an odd number of letters, the middle letter is also included in the first half.

Using the row asdfghjkl as an example, the word gafs can be typed with the left hand.

Scoring

This is . Make your code as short as possible.

Test Cases

These are formatted as \$K\$, \$S\$ -> (expected output)

[qwertyuiop, asdfghjkl, zxcvbnm], qazg -> true
[qwertyuiop, asdfghjkl, zxcvbnm], qpazg -> false
[p, asdfghjklqwertyuio, zxcvbnm], sxzklkl -> true
[p, asdfghjklqwertyuio, zxcvbnm], b -> false
[qazwsxplkm, edocrfv, tgbyhnuji], bad -> true
[qazwsxplkm, edocrfv, tgbyhnuji], tex -> false
[thequick, brownfx, jmpsvlazydg], brow -> true
[thequick, brownfx, jmpsvlazydg], fox -> false
\$\endgroup\$
  • \$\begingroup\$ qazwsxplkm, edcrfv, tgbyhnuji doesn't contains letter o? So will there be a testcase, like, [qazwsxplkm, edcrfv, tgbyhnuji], zoo? \$\endgroup\$ – tsh May 11 at 4:59
  • 4
    \$\begingroup\$ I just typed this entire comment with my left hand. :) \$\endgroup\$ – intuited May 11 at 13:52
  • 3
    \$\begingroup\$ "solely of letters appearing in the first half of each row". So, unlike in real life (first 5 characters), the Example1, "zxcvbnm", means that the letter "b" is not a left-hand letter. \$\endgroup\$ – Ray Butterworth May 11 at 15:30
  • 4
    \$\begingroup\$ Relevant XKCD \$\endgroup\$ – Mark May 12 at 0:00
  • 1
    \$\begingroup\$ @Kaddath Keyboard with more rows: en.wikipedia.org/wiki/… \$\endgroup\$ – tsh May 12 at 1:50

18 Answers 18

8
\$\begingroup\$

Python 3, 48 bytes

Takes as input \$ K \$ and \$ S \$, the keyboard and the target string. \$ K \$ is a taken as a list of lists.

lambda K,S:{r.pop(0)for r in K for i in r}>={*S}

Try it online!

Explanation

It essentially converts \$ S \$ and the valid keyboard characters \$ K' \$ into sets, and returns True iff \$ S \$ is a subset of \$ K' \$. To obtain only the first half of each row of \$ K \$, we use the pop trick, which is explained in this answer of mine.


The previous answer, where \$ K \$ is taken as a list of strings instead.

Python 3, 54 bytes

lambda K,S:{*''.join(r[:-~len(r)//2]for r in K)}>={*S}

Try it online!

| improve this answer | |
\$\endgroup\$
6
\$\begingroup\$

J, 22 21 bytes

a:=(-.>.@-:@#$])~&.>/

Try it online!

-1 byte thanks to FrownyFrog


Takes input as boxed words, with the string to test at the end.

Reduces the list, set-subtracting -. the first half -.>.@-:@# of each row $] from the string to test.

Check if the result is empty: a:=

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Here also {. can be $ \$\endgroup\$ – FrownyFrog May 11 at 6:55
5
\$\begingroup\$

05AB1E, 8 bytes

-2 thanks to Kevin Cruijssen.

ε2ä¬}JÃQ

Try it online!

Explanation

ε          For each item of the input list:
 2ä        Split into chunks of size 2
           (conveniently, the middle character is included in the left half)
   ¬}      Take the head of this list
     J     Join the output string
      Ã    List intersection with the input
       Q   is the input
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ -2 by using ÃQ instead of s€åß \$\endgroup\$ – Kevin Cruijssen May 11 at 7:18
4
\$\begingroup\$

Bash + Unix utilities, 57 bytes

s()(echo ${1:0:(${#1}+1)/2});grep ^[`s $1``s $2``s $3`]*$

Try it online!

The three keyboard rows are passed as arguments, and the input string is passed on stdin.

Output is the exit code (0 for truthy, 1 for falsey).

This can probably be improved with clever golfing.

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

Jelly, 9 bytes

ŒH€ZḢFfƑ@

A dyadic Link accepting a list of lists of characters on the left and a list of characters on the right which yields 1 if the right may be typed with the left hand or 0 if not.

Try it online!

How?

ŒH€ZḢFfƑ@ - Link: keys, word
ŒH€       - halve each
   Z      - transpose
    Ḣ     - head
     F    - flatten
        @ - with swapped arguments:
       Ƒ  -   is invariant under?:
      f   -     filter keep
| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

Retina 0.8.2, 42 34 bytes

r`(?<-1>.)+(?<=(\w\w)+),
,
D`\w
,$

Try it online! Link includes test cases. Explanation:

r`(?<-1>.)+(?<=(\w\w)+),
,

The r modifier causes the regex to be processed from right to left, so that the , is matched first, before the lookbehind then matches as many pairs of letters as possible. .NET captures each matching pair into a stack of values for capture group 1. The balancing group then matches once for each value in the stack, thus deleting 1 character from the end of the row for every pair of letters.

D`\w

Remove all duplicate letters.

,$

Check that no letters of S remain.

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

C (GCC) 114 109 108 105 102 bytes

-3 bytes ceilingcat

i,h,t,r;f(char**k,char*s){for(r=1;*s;++s)for(i=3;i--;)for(h=strlen(k[i]);t=k[i][++h/2];)r*=t!=*s;i=r;}

Try it online!

| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

Google Sheets, 106 99 Bytes

=ArrayFormula(1-IsErr(Or(Find(Mid(B1,Row(Offset(A1,0,0,Len(B1))),1),Join(,Left(A:A,Round(Len(A:A)/2

Keyboard input is in the range A1:A3. The word is input in B1. After entering the formula, Sheets will automatically add 7 trailing parentheses.

enter image description here

Join(,Left(A:A,Round(Len(A:A)/2))) combines the left-hand side of the keyboard rows into a single string we can search later.

Mid(B1,Row(Offset(A1,0,0,Len(B1))),1) pulls each letter of the word, one character at a time.

Or(Find(Mid(~),Join(~))) searches for each character in the search string and returns the location of each (a positive integer). If it can't find the character, it returns the #VALUE! error. Therefore, Or(~) returns either TRUE or #VALUE!.

1-IsErr(Or(~)) returns 1 for TRUE and 0 for #VALUE!.

ArrayFormula(~) makes all these pieces work on input and output arrays instead of individual cells. This only matters for joining the input keyboard into a string and pulling out one character at a time from the word.


| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

JavaScript (Node.js), 51 bytes

k=>s=>s.every(c=>k.some(l=>l.length/l.search(c)>2))

Try it online!

Input keyboard as an array of three strings. Input the word as an array of characters.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

x86 32-bit machine code: 34 31 bytes

Strategy: build a bitmap of left-hand keys with bit test-and-set (bts), then loop over the input string and check against that bitmap (bt). 386 bt* instructions with a register destination mask the bit-index like shifts do, so we can use ASCII characters directly as bit indices. (0x61 .. 0x7a)

Inputs:

  • EDI: const char *str (0-terminated C string)
  • ESI: points at rows serialized into 3 back-to-back explicit-length strings in a buffer. 3x { dword length, char[] }, no indirection just flat byte stream concatenated to be loaded with lodsd / lodsb. Asm wrapper to do this conversion from an array of 3x char* included with the test harness on TIO.

Output: AL=first non-left-hand character (non-zero=falsy), or zero=truthy (the terminator). Assembly language can just as easily jump on non-zero as on zero so this is justified. An alternate version of this took an explicit-length string and returned 0 or 1 in the CF flag.

Clobbers: all GP registers except ESP

 1                                  tlh:
 2 00000000 31DB                        xor   ebx, ebx              ; left hand keys bitmap
 3 00000002 8D4B03                      lea   ecx, [ebx + 3]
 4                                  .rows:
 5 00000005 AD                          lodsd              ; length.  Upper bytes zero because each letter can appear at most once.
 6 00000006 8D2C06                      lea   ebp, [esi + eax]
 7 00000009 92                          xchg  edx, eax
 8                                    .keys:                   ; do{
 9 0000000A AC                              lodsb
10 0000000B 0FABC3                          bts   ebx, eax      ; bmap |= 1<<(c & 31)
11 0000000E 4A                              dec   edx
12 0000000F 4A                              dec   edx
13 00000010 7FF8                            jg   .keys        ; while(len-=2 > 0);  // handles the len=0 case where --len becomes negative
14 00000012 89EE                        mov   esi, ebp      ; skip 2nd half of string
15 00000014 E2EF                        loop  .rows
16                                  
17                                  ;;; If the input string indexes any bit in EBX that isn't set, it's not left-hand typeable
18 00000016 89FE                        mov   esi, edi
19                                  .chars:               ; do{
20 00000018 AC                          lodsb
21 00000019 0FA3C3                      bt    ebx, eax
22 0000001C 72FA                        jc  .chars
23                                  .exit:
24                                      ; non-zero AL means we found a non-left-hand character.
25                                      ; zero means we found the terminating 0.  ASCII 'a' = 0x61 so our bitmap always has bit 0 = 0
26 0000001E C3                          ret

TODO:

  • A better row input format to save instructions in the input code? (When I designed this format, I forgot about having to skip over the right half of a row that I wasn't going to read. lea ebp, [start + len] + mov was a bugfix. It would be nice to at least not need that many regs.)
  • Maybe start with an all-1 bitmap and clear bits for right-hand keys? But that introduces a corner case for a row of length 1 which has no right-hand keys. Otherwise it might allow efficient looping to the end of a 0-terminated string.

I used the nice C test harness from @Noodle9's answer. This is 32-bit asm so it would be a double pain to get something runnable on TIO.run with NASM. (You can use FASM to make a 32-bit executable directly, without a separate linker invocation, but I think that doesn't link libc. You could I guess inline a simple strlen, or hard-code some data structures to at least exit with a 0 / 1 exit status...) I put the source + asm wrapper (to adapt to a normal C calling convention), and C test harness itself on TIO even though you can't actually try it there.

$ nasm -f elf32 -l/dev/stdout type-left-hand.asm &&
 gcc -Wall -fno-pie -no-pie -m32 type-left-hand.[co] &&
 ./a.out
qwertyuiop asdfghjkl zxcvbnm
qazg -> 1 (1)
qpazg -> 0 (1)
p asdfghjklqwertyuio zxcvbnm
sxzklkl -> 1 (1)
b -> 0 (1)
qazwsxplkm edocrfv tgbyhnuji
bad -> 1 (1)
tex -> 0 (1)
thequick brownfx jmpsvlazydg
brow -> 1 (1)
fox -> 0 (1)

0 / 1 (1) means left-hand typeable (matches the correct result)

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

C (gcc), 116 \$\cdots\$ 108 104 bytes

Saved 4 bytes thanks to rtpax!!!
Saved 4 bytes thanks to ceilingcat!!!

c;p;l;r;i;f(char**k,char*s){for(r=1;*s;++s)for(i=3;i--;r&=l>p*2)for(l=p=0;c=k[i][l];++l)p=c-*s?p:l;c=r;}

Try it online!

Takes the keyboard as an array of strings and the word as a string.
Returns \$1\$ for true and \$0\$ otherwise.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ 112 brings you shorter than mine \$\endgroup\$ – rtpax May 11 at 12:23
  • \$\begingroup\$ @rtpax Going the other way with i - nice one, thanks! :-) \$\endgroup\$ – Noodle9 May 11 at 12:45
1
\$\begingroup\$

Haskell, 69 bytes

x l=take(length l`div`2)l;l k s=all(\c->elem c$foldl(++)[]$map x k)s

Use (in ghci):

$ let kbd = ["qwertyuiop","asdfghjkl;","zxcvbnm,./"]
$ l kbd "stewardesses"
=> True
$ l kbd "joker"
=> False
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ foldl(++)[] is the same as concat. concat$map is the same as concatMap. Finally, concatMap x k is the same as just x=<<k. \$\endgroup\$ – Laikoni Jun 18 at 7:37
  • \$\begingroup\$ l k s=all(...)s can be eta-reduced to l k=all(...). (\c->elem c$x=<<k) can be shortened to (`elem`(x=<<k)). \$\endgroup\$ – Laikoni Jun 18 at 7:40
  • \$\begingroup\$ Using pointfree.io, it turns out x l=take(length ldiv2)l can be equivalently written as take=<<(`div`2).length and thus be inlined in l. \$\endgroup\$ – Laikoni Jun 18 at 7:44
  • \$\begingroup\$ Using pointfree.io yet again, we arrive at a 44 byte version: all.flip elem.((take =<<(`div`2).length)=<<). Try it online! \$\endgroup\$ – Laikoni Jun 18 at 7:47
1
\$\begingroup\$

C# (Visual C# Interactive Compiler), 56 63 bytes

k=>s=>s.All(c=>k.Any(l=>(uint)l.IndexOf(c)/(float)l.Length<.5))

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I think you need to include the usings in your byte count. In this case, I think you can inline them though \$\endgroup\$ – math junkie May 15 at 3:18
  • \$\begingroup\$ Admittedly, I don't really golf in C#, so there might be a shorter way than what I'm suggesting. I'd recommend checking out Tips for golfing in C# if you haven't already \$\endgroup\$ – math junkie May 15 at 3:19
  • \$\begingroup\$ Thanks for the tips! But if I can't use the usings I think I'll just write their keywords instead of the full types 😊 \$\endgroup\$ – Netråm May 15 at 13:15
0
\$\begingroup\$

JavaScript (ES6), 53 bytes

Takes input as (keyboard)(word). Returns a Boolean value.

k=>w=>!w.match(`[${k.map(r=>r.slice(-r.length/2))}]`)

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Charcoal, 12 bytes

⬤η⊙θ№…λ⊘⊕Lλι

Try it online! Link is to verbose version of code. Takes input as a list and a string. Output is a Charcoal boolean, i.e. - for true, nothing for false. Explanation:

 η              Input S
⬤               All characters 
   θ            Input `K`
  ⊙             Any row
      λ         Current row
     …          Truncated to
       ⊘⊕Lλ     Half its length rounded up
    №      ι    Contains input character
                Implicitly print
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Python 3, 53 bytes

lambda k,w:all(max(map(r.find,w))<len(r)/2for r in k)

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Python 2, 57 55 bytes

-2 bytes thanks to @ovs !

lambda k,s:all(t.find(c)*2<len(t)for t in k for c in s)

Try it online!

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ 2 bytes shorter with t.find(c)*2<len(t). \$\endgroup\$ – ovs May 11 at 7:04
0
\$\begingroup\$

R, 72 71 bytes

function(k,s)all(t(sapply(k,function(x)match(s,x,0)))<(lengths(k)+2)/2)

Try it online!

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.