The keitai input method is a method for writing Japanese kana on a 12-key phone keypad. Similar to Western keypads, each kana is assigned to a key; when the key is pressed multiple times, it cycles between all kana assigned to that key.

You will be using the following key-kana assignments, with this order of kana (based on this layout):

Key Kana
1 あいうえお
2 かきくけこ
3 さしすせそ
4 たちつてと
5 なにぬねの
6 はひふへほ
7 まみむめも
8 やゆよ
9 らりるれろ
0 わをん
* ゛゜小

Dakuten, Handakuten, and Small Kana

The * character (or some other non-digit character of your choice) will apply a dakuten () or handakuten (), or make small () the kana before it. If a form can't be applied to the kana before it, it is skipped in the cycle.

Kana with dakuten, handakuten, and small forms are in the table below.

Key Kana Dakuten Handakuten Small
1 あいうえお ぁぃぅぇぉ
2 かきくけこ がぎぐげご
3 さしすせそ ざじずぜぞ
4 たちつてと だぢづでど
6 はひふへほ ばびぶべぼ ぱぴぷぺぽ
8 やゆよ ゃゅょ

Note: is the only t-series kana to have a small form. Input 4** gives because cannot be shrunk; but the input 444** gives small .


A list of strings, with each string containing a series of the same digit.


The hiragana represented by the input.

Test Cases

['2','5'] => 'かな'
['222222','555555'] => 'かな'
['1','99','2','*','44444','111'] => 'ありがとう'
['4','*','2','*','22222','44444','0','999'] => 'だがことわる'
['11111','7','1111','00','33','000','4444', '***','999'] => 'おまえをしんでる'
['2','2','99','7','333'] => 'かかります'
['1','****'] => 'あ'
['4','**'] => 'た'
['444','**'] => 'っ'
['555','***********************'] => 'ぬ'
['22','*','88','*','111','55','88','*','111'] => 'ぎゅうにゅう'
['8','444','4'] => 'やつた'
['8','444','*','4'] => 'やづた'
['8','444','**','4'] => 'やった'
['6'] => 'は'
['6','*'] => 'ば'
['6','**'] => 'ぱ'
['6','***'] => 'は'

Other Rules

  • Input can be any reasonable method (list of strings, list of integers, a big string with delimiters, list of run-length-encoded strings, etc.)
  • Output can be any reasonable method (printing to STDOUT, returning a string, etc.)
  • This is , so shortest code wins!
  • 2
    \$\begingroup\$ Can we take digits and *'s in the same string? (e.g. ['9','444*','4'] instead of ['9','444','*','4']) \$\endgroup\$
    – Arnauld
    Jun 30, 2023 at 21:41
  • 3
    \$\begingroup\$ @Arnauld yes, digits + *s in the same string is allowed \$\endgroup\$
    – bigyihsuan
    Jun 30, 2023 at 21:46
  • 2
    \$\begingroup\$ @Arnauld yea that's a mistake, it's been fix (やゆよ are under 8) \$\endgroup\$
    – bigyihsuan
    Jul 1, 2023 at 1:35
  • 3
    \$\begingroup\$ should be fixed now \$\endgroup\$
    – bigyihsuan
    Jul 1, 2023 at 1:49
  • 2
    \$\begingroup\$ @Arnauld fixed as well \$\endgroup\$
    – bigyihsuan
    Jul 1, 2023 at 4:10

6 Answers 6


JavaScript (Node.js), 180 bytes

Expects a list of strings, where * sequences are concatenated with digit sequences.

Returns a list of kana.

a=>a.map(s=>String.fromCharCode(Buffer("n *4>IN]bh")[n=+s[0]]+12321+(p=s.lastIndexOf(n),q=s.length+~p,p%=n%8?5:3)*(m=1855**n%79&3)+(n-4?p&&2*!n:q%(m+=p==2)>1?-2:p>1)+(q%m^n%7==1)))

Try it online!


Interpreting the input string

Given the input string \$s\$, we compute:

  • \$p\$: the 0-based position of the last digit in \$s\$
  • \$q\$: the length of \$s\$, minus \$p\$ and minus \$1\$

Base code point

We use a lookup Buffer and a fixed offset to get the base code point for the digit \$n\$:

Buffer("n *4>IN]bh")[n] + 12321

Try it online!

Length of the digit cycle

The length of the digit cycle is \$5\$, except when \$n=0\$ or \$n=8\$, in which case it's \$3\$:

n % 8 ? 5 : 3

We reduce \$p\$ by the above modulo, which gives the 0-based index in the digit cycle.

Length of the * cycle

For the length \$m\$ of the * cycle, we use the following formula:

1855 ** n % 79 & 3

which, with a couple of IEEE-754 approximation errors, gives:

[ 1, 2, 2, 2, 2, 1, 3, 1, 2, 1 ]

The 0-based index in the * cycle is \$q \bmod m\$.

Final code point

The generic formula for the final code point is:

base_code_point + p * m + (q % m)

But there are several special cases which are detailed below.

Case \$n=4\$, \$p=2\$

For the special case where the digit \$4\$ is pressed \$3\$ times to generate the small kana っ, we need to increment \$m\$ and to add a correction offset to the final result:

+ (n - 4 ? ... : q % (m += p == 2) > 1 ? -2 : p > 1)

Case \$n=0\$

The code points for \$n=0\$ have a specific offset (\$+2\$) when \$p>0\$:

+ (n - 4 ? p && 2 * !n : ...)

Case \$n=1\$ or \$n=8\$

If \$n=1\$ or \$n=8\$, each pair of code points is reversed, leading to the sequence:


In this case, we add \$(q \bmod m)\operatorname{XOR}1\$ instead of \$q \bmod m\$:

+ (q % m ^ n % 7 == 1)

Raku, 330 295 248 240 bytes

*.map:{/(.)(\d)*(_)*/;uniparse 'hiragana letter '~({TR/ksth/gzdb/},{S/h/p/},'small '~*,~*)[flat
^4 Zxx($0 X~~2|3|4|6,6,1|8|{$_*9+$1%5==38},!0)][($2-1)%*]([<wa wo n>,|map((*X~
<a i u e o>),"",|'kstnhmr'.comb),<ya yu yo>][|^8,9,8][$0][$1%*])}

Try it online!

An underscore is used as the single non-digit character. A sequence of underscores is taken to be appended to the preceding digits rather than being its own element in the input array.

  • *.map: { ... } converts each input element into a single Japanese character.
  • /(.)(\d)*(_)*/ matches the given pattern against each input element. The match is assumed to always succeed. Afterwards, $0 is the first digit, $1 is a list of succeeding digits, and $2 is a list of underscores. In Raku, lists are treated as numbers equal to their length when used in an arithmetic context, so $1 can be interpreted as one less than the number of initial digits, and $2 as the number of trailing underscores.
  • [<wa wo n>,|map(...),<ya yo yu>] constructs an array that maps digits to the list of romanized characters in the first "Kana" column given above. For example, 1 maps to the list <a i u e o>, 2 to the list <ka ki ku ke ko>, 8 to the list <ya yu yo>, etc. For brevity, the kana groups are generated out of order, so [|^8,9,8] slices the original list into the correct order. The given digit is used to look up the appropriate list of kana ([$0]), and the number of repetitions of the digit indexes into that list, wrapping around to the front of the list as needed ([$1 % *]).
  • A list of anonymous transformation functions is consulted. That's the list ({ TR/ksth/gzdb/ }, { S/h/p/ }, 'small ' ~ *, ~*). The first function transforms a kana into its dakuten form (by translating "k" to "g", "s" to "z", etc), the second one transforms it into its handakuten form (by replacing "h" with "p"), the third one makes the kana small (by prepending "small " to it), and the last one ~* is a string-identity function that circles back to the first version of the kana. Each function except the last is only conditionally included in the list by the following array slice. For example, if the first digit is 1, only the third and fourth transformations are applicable; if it's 6, all but the third are. The appropriate function for the number of trailing underscores is called, again wrapping around back to the beginning as needed with [($2 - 1) % *].
  • Finally the built-in uniparse function is called to convert the Unicode name of the character to the actual Unicode character, after prepending it with the common prefix "hiragana letter ".

JavaScript (Node.js), 229 bytes

X=['854'];for(M=i=9;i;U=(x,y)=>x[~-y.length%x.length])for(Y=X[i--]=[j=i-7?5:3];j;)for(k of(Z=Y[--j]=[])+(i%7?i<4?M-49?21:213:i-5?1:321:12))Z[k-1]=++M

Try it online!

0 is different from unicode-table so hardcoded


C (GCC), 232 228 223 * bytes

f(char**s){for(;**s;){int c=**s-48,n=~-strlen(*s)%(c&7?5:3),a=**++s-42?0:strlen(*s),k='\x06v\xa9'>>2*c&3;s+=a>0;a%=c-4|n-2?k:3;printf("\xe3%c%c",(c&8||c<1)+129,"\x91\x82\x8B\x95\x9F\xAA\xAF\xBE\x84\x89"[c]+k*n-(c-4|n-2|a-2?c%7-1?-a:a:1)-(c-4||n<2)-(c+n?-1:1));}}

Attempt This Online!

*: you have to encode each char in the hex string as a single byte instead of an escape code; I wasn't able to copy/paste it correctly on ATO.

How to test it: run gcc a.c && ./a.out | grep -Eva '(\b.+) \1\b'. Outputs each testcase for which the function outputs an incorrect string.

  • \$\begingroup\$ printf("%c%c%c",227, => printf("\xe3%c%c", \$\endgroup\$
    – l4m2
    Jul 1, 2023 at 14:29

Charcoal, 132 bytes

WS℅⁺¹²³¹⁵℅§§⪪§⪪”}∧﹪IC↗T″§×K_✳Y59G%⸿u-↷*f↨V±IFl⟲⦃⌊CψR^4ΦIhGIT↧P{$QTⅈZ?≕⌈⟧2EχPP⊖▷ζQ§⎇Z∨⁰\`⪪k>+²γ~δïwⅈV-⦃ïB.|←⁴G⭆!±W⁹✂φβQ²1”¶I⌈ι №ι⌈ι№ι*

Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated strings of digits and *s. Explanation:

WS                                  Loop over each line
               ”...”                Compressed look-up table
              ⪪     ¶               Split on newlines
             §                      Indexed by
                       ι            Current string
                      ⌈             Maximum i.e. first character
                     I              Cast to integer
            ⪪                       Split on spaces
           §                        Indexed by
                         №          Count of
                            ι       Current string
                           ⌈        Maximum i.e. first character
                          ι         In current string
          §                         Indexed by
                             №      Count of
                               *    Literal string `*`
                              ι     In current string
         ℅                          Take the ASCII code
   ⁺                                Plus
    ¹²³¹⁵                           Literal integer `12315`
  ℅                                 Convert to Unicode
                                    Implicitly print

The look-up table is adjusted for the 1-indexing of the button value and number of button presses, although the number of *s remains 0-indexed.


Python 3 + romkan, 720 bytes

import romkan
def f(x):
	for i in range(len(x)):
		if j[0].isdigit():
			if j[0]in"12345679":s+="oaiue"[len(j)%5]
			if s[-2]in"aeiou'"and len(j)%2and s[-1]in"aeiou":s=s[:-1]+"ぁぃぅぇぉ"["aiueo".index(s[-1])]
			elif s[-2]in"ks"and len(j)%2:s=s[:-2]+"gz"["ks".index(s[-2])]+s[-1]
			elif s[-2:]=="tu"and len(j)%3:s=s[:-2]+"っ"if len(j)%3==2 else s[:-2]+"du"
			elif s[-2]=="h":s=s[:-2]+"hbp"[len(j)%3]+s[-1]
			elif s[-2]=="t"and len(j)%2:s=s[:-2]+"d"+s[-1]
			elif s[-2]=="y"and len(j)%2:s=s[:-2]+"ゃゅょ"["auo".index(s[-1])]
	return romkan.to_hiragana(s[1:])

Try it online! (without romkan and romkan.to_hiragana)


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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