For strings s and t, we will say that s and t are prefix-free when neither is a prefix of the other.
Let L be a positive integer. A set of strings S is a good string set when the following conditions hold true:
 - Each string in S has a length between 1 and L (inclusive) and consists of the characters 0 and 1.
 - Any two distinct strings in S are prefix-free.
We have a good string set S = \{ s_1, s_2, ..., s_N \}. Alice and Bob will play a game against each other. They will alternately perform the following operation, starting from Alice:
 - Add a new string to S. After addition, S must still be a good string set.
The first player who becomes unable to perform the operation loses the game. Determine the winner of the game when both players play optimally.

-----Constraints-----
 - 1 \leq N \leq 10^5
 - 1 \leq L \leq 10^{18}
 - s_1, s_2, ..., s_N are all distinct.
 - { s_1, s_2, ..., s_N } is a good string set.
 - |s_1| + |s_2| + ... + |s_N| \leq 10^5

-----Input-----
Input is given from Standard Input in the following format:
N L
s_1
s_2
:
s_N

-----Output-----
If Alice will win, print Alice; if Bob will win, print Bob.

-----Sample Input-----
2 2
00
01

-----Sample Output-----
Alice

If Alice adds 1, Bob will be unable to add a new string.

Difficulty: competition

Starter code:

Solutions:
import sys
input=sys.stdin.readline
sys.setrecursionlimit(10**9)
from collections import deque

class Node:
    def __init__(self,depth):
        self.depth=depth
        self.left=None
        self.right=None

def insert(node,s):
    n=node
    for i in range(len(s)):
        t=s[i]
        if t=='0':
            if n.left is None:
                n.left=Node(i+1)
            n=n.left
        else:
            if n.right is None:
                n.right=Node(i+1)
            n=n.right

class Trie:
    def __init__(self):
        self.root=Node(0)
    def insert(self,s:str):
        insert(self.root,s)

n,l=map(int,input().split())
S=[input().strip() for _ in range(n)]
trie=Trie()
for s in S:
    trie.insert(s)
Data=[]
q=deque([trie.root])

def dfs(node):
    if node.right is None and node.left is None:
        return
    if node.right is None or node.left is None:
        Data.append(l-node.depth)
    if node.right:
        q.append(node.right)
    if node.left:
        q.append(node.left)

while q:
    dfs(q.popleft())
xor=0

def Grundy(n):
    ret=1
    while n%2==0:
        n//=2
        ret*=2
    return ret

for i in Data:
    xor^=Grundy(i)
print('Alice' if xor else 'Bob')

import sys
#from collections import defaultdict
sys.setrecursionlimit(10**6)

def input():
	return sys.stdin.readline()[:-1]
n, l = list(map(int, input().split()))
ss = [list(input())[::-1] for _ in range(n)]

def addTree(tree, sentence):
	if not sentence:
		return None   

	if sentence[-1] not in tree:
		tree[sentence[-1]] = {}

	p = sentence.pop()
	tree[p] = addTree(tree[p], sentence)

	return tree

def createTree(sentences):    
	tree = {}
	for sentence in sentences:
		tree = addTree(tree, sentence)

	return tree

tree = createTree(ss)
#print(tree)

grundy = 0
def dfs(cur, level):
	nonlocal grundy
	if cur is None:
		return
	elif len(cur) == 1:
		grundy ^= (l-level) & (-l+level)
	for k in list(cur.keys()):
		dfs(cur[k], level+1)
	return
	
dfs(tree, 0)

if grundy == 0:
	print("Bob")
else:
	print("Alice")


import sys
sys.setrecursionlimit(2 * 10 ** 5)


class TrieNode:
    def __init__(self, char):
        self.char = char
        self.nextnode = dict()
        self.is_indict = False


class Trie:
    def __init__(self, charset):
        self.charset = charset
        self.root = TrieNode('')

    def add(self, a_str):
        node = self.root
        for i, char in enumerate(a_str):
            if char not in node.nextnode:
                node.nextnode[char] = TrieNode(char)
            node = node.nextnode[char]
            if i == len(a_str) - 1:
                node.is_indict = True

    def dfs(self, node, dep):
        ret, cnt = 0, 0
        if node.is_indict:
            return 0
        for s in '01':
            if s not in node.nextnode:
                cnt += 1
            else:
                ret ^= self.dfs(node.nextnode[s], dep + 1)
        height = L - dep
        if cnt % 2:
            power2 = 0
            while height > 0 and height % 2 == 0:
                power2 += 1
                height //= 2
            ret ^= 2 ** power2
        return ret

    def debug_output(self, node, now):
        print((node.char, list(node.nextnode.items()), node.is_indict, now))
        if node.is_indict:
            print(now)
        for n in list(node.nextnode.values()):
            self.debug_output(n, now + n.char)

N, L = list(map(int, input().split()))
T = Trie('01')
for _ in range(N):
    T.add(input())
# T.debug_output(T.root, '')
print(("Alice" if T.dfs(T.root, 0) else "Bob"))


n,l = map(int,input().split())
ls = [input() for i in range(n)]
sm = sum([len(s) for s in ls])
ls.sort()
num = [0]*(sm+1)
for i in range(n):
  if i == 0:
    for j in range(len(ls[i])):
      num[j+1] = 1
  else:
    x = ls[i]
    p = ls[i-1]
    cnt = 0
    while cnt < min(len(x),len(p)):
      if x[cnt] == p[cnt]:
        cnt += 1
      else:
        break
    num[cnt+1] -= 1
    for j in range(cnt+2,len(x)+1):
      num[j] += 1
ans = 0
def d2(x):
  if x == 0:
    return 0
  cnt = 0
  while x%2 == 0:
    cnt += 1
    x //= 2
  return 2**cnt
for i in range(sm+1):
  ans ^= d2(l+1-i)*(num[i]%2)
if ans:
  print("Alice")
else:
  print("Bob")

def getGrundyNumber(x):
    ans = 1
    while x % (ans * 2) == 0:
        ans *= 2
    return ans


N, L = list(map(int, input().split()))
Ss = [input() for i in range(N)]

# トライ木を作成する
Trie = [[-1, -1]]
for S in Ss:
    iT = 0
    for c in map(int, S):
        if Trie[iT][c] == -1:
            Trie += [[-1, -1]]
            Trie[iT][c] = len(Trie) - 1
        iT = Trie[iT][c]

# 子が１つの頂点を探す
Hgts = {}
stack = [(0, L + 1)]
while stack:
    iT, Hgt = stack.pop()
    num = 0
    for c in Trie[iT]:
        if c != -1:
            stack.append((c, Hgt - 1))
            num += 1

    if num == 1:
        Hgts[Hgt - 1] = Hgts.get(Hgt - 1, 0) + 1

# Grundy数のXORを求める
ans = 0
for Hgt, num in list(Hgts.items()):
    if num % 2:
        ans ^= getGrundyNumber(Hgt)

if ans:
    print('Alice')
else:
    print('Bob')


from collections import defaultdict


def solve(n, l, nums):
    xor = 0

    if n == 1:
        for i in range(min(nums), l + 1):
            xor ^= i & -i
        return xor

    for i in range(min(nums), l + 1):
        ni = nums[i]
        for k in ni:
            if k ^ 1 not in ni:
                xor ^= i & -i
            nums[i + 1].add(k // 2)
        del nums[i]
    return xor


n, l = list(map(int, input().split()))
nums = defaultdict(set)
for s in (input() for _ in range(n)):
    nums[l - len(s) + 1].add(int(s, 2) + (1 << len(s)))

print(('Alice' if solve(n, l, nums) else 'Bob'))


from collections import deque
# 節の定義
class Node:
    def __init__(self, x):
        self.data  = int(x)
        self.left  = None
        self.right = None
# 挿入
def insert(node, x):
    ima = node
    for i in range(len(x)):
        y = x[i]
        if y == "1":
            if ima.right is None:
                ima.right = Node(i+1)
            ima = ima.right
        else:
            if ima.left is None:
                ima.left = Node(i+1)
            ima = ima.left



class Trie:
    def __init__(self):
        self.root = Node(0)

    # 挿入
    def insert(self, x):
        insert(self.root, x)

n,l = map(int,input().split())
s = [input() for i in range(n)]

trie = Trie()
for i in s:
    trie.insert(i)
data = []
que = deque([trie.root])
def dfs(node):
    if node.right is None and node.left is None:
        return 0
    if node.right is None or node.left is None:
        if l-node.data:
            data.append(l-node.data)
    if node.right:
        que.append(node.right)
    if node.left:
        que.append(node.left)
    
while que:
    dfs(que.popleft())
xor = 0
def nimber(n):
    x = 1
    while n%2 == 0: n//= 2; x*= 2
    return x
for i in data:
    xor ^= nimber(i)
print("Alice" if xor else "Bob")

# nimber: 0, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1, 16, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1, 32, ...

class Node:
    def __init__(self, size, avail):
        self.end = False
        self.nxt = [None] * size
        self.avail = avail
    def __setitem__(self, i, x): self.nxt[i] = x
    def __getitem__(self, i): return self.nxt[i]

def makeTrie(strs, size):
    root = Node(size, l)
    for s in strs:
        n = root
        for c in s:
            i = int(c) # Change this to adapt. ex: ord(c)-ord("A")
            if not n[i]: n[i] = Node(size, n.avail-1)
            n = n[i]
        n.end = True
    return root

def nimber(n):
    x = 1
    while n%2 == 0: n//= 2; x*= 2
    return x

input = __import__('sys').stdin.readline
n, l = map(int,input().split())
strs = [input().rstrip() for i in range(n)]
T = makeTrie(strs, 2)

ans = 0
stack = [T]
while stack:
    n = stack.pop()
    for i in 0, 1:
        if not n[i]: ans^= nimber(n.avail)
        elif not n[i].end: stack.append(n[i])
print("Alice" if ans else "Bob")

class Node():
    def __init__(self):
        self.lt = None
        self.rt = None
        self.dep = None

class Trie():
    def __init__(self):
        self.root = Node()
        self.root.dep = 0

    def add(self, string):
        node = self.root
        for s in string:
            if s == '0':
                if node.lt is None:
                    node.lt = Node()
                    node.lt.dep = node.dep + 1
                node = node.lt
            else:
                if node.rt is None:
                    node.rt = Node()
                    node.rt.dep = node.dep + 1
                node = node.rt

N, L = map(int, input().split())

trie = Trie()

for _ in range(N):
    s = input()
    trie.add(s)

subgame = []
stack = [trie.root]

while stack:
    node = stack.pop()
    if node.lt is None and node.rt is None:
        continue
    elif node.rt is None:
        stack.append(node.lt)
        subgame.append(L - node.dep)
    elif node.lt is None:
        stack.append(node.rt)
        subgame.append(L - node.dep)
    else:
        stack.append(node.lt)
        stack.append(node.rt)

def grundy(n):
    if n == 0: return 0
    if n % 2 == 1: return 1
    if n == 2**(n.bit_length() - 1):
        return n
    return grundy(n - 2**(n.bit_length() - 1))

g = 0

for l in subgame:
    g ^= grundy(l)

print('Alice' if g != 0 else 'Bob')

import sys
N, L = list(map(int, input().split()))
sys.setrecursionlimit(200000)

class Node:
    def __init__(self, depth=0):
        self.parent = None
        self.children = {}
        self.depth = depth
    def add(self, n):
        self.children[n] = Node(self.depth+1)
    def has(self, n):
        return n in self.children
t = Node()
for i in range(N):
    s = input()
    nt = t
    for c in s:
        if not nt.has(c):
            nt.add(c)
        nt = nt.children[c]

def solve(node):
    nonlocal N, L
    ans = 0
    f = lambda d: (L - d) & -(L - d)
    if node.has('0'):
        ans ^= solve(node.children['0'])
    else:
        ans ^= f(node.depth)
    if node.has('1'):
        ans ^= solve(node.children['1'])
    else:
        ans ^= f(node.depth)
    return ans

ans = solve(t)
if ans == 0:
    print('Bob')
else:
    print('Alice')



"""
Writer: SPD_9X2
https://atcoder.jp/contests/arc087/tasks/arc087_c

初期状態で出ている数字を木としてあらわす
数字でふさがっている所はもう追加できない
まだおけるのは、空いている部分だけ

Lがでかいので、深さを考えていては死ぬ

そのままふさぐと、置ける場所は1減る
伸ばしておけば、Lまで増やして置ける

置ける場所の偶奇か？
置ける場所が0で来たら負け→初手が奇数なら先手勝ち・そうでないなら後手勝ち？
伸ばせる奴は偶奇反転に使える

部分で考えてみるか→

Grundy数計算
深さ1の部分木のGrundy → 1
深さ2の部分木のGrundy → 2
深さ3　　　　　　　　 → 1 ?
深さ4

Grundyで行ける = xorが全体のGrundyになるという仮定
なので、深さxを試すときは、深さxで1回で行ける状態のxorのmexがGrundyとなる

深さ1なら、問答無用で1
深さ2なら、0 or 1 になるので 2
深さ3なら, 0 or 2 になるので 1
深さ4なら, 0 or 1 or 3 or 2 なので 4
深さ5なら, 0 or 4 or 5 or 7 or 6 で 1
深さ6なら, 0 or 1 or 5 or 4 or 6 or 7 で 2
深さ7なら, 0 or 2 or 3 or 7 or 6 or 4 or 5 で 1
深さ8なら, 0 or 1 or 3 or 2 or 6 or 7 or 5 or 4 で 8

→どうやら、その数字を割り切れる最大の2の階乗数がGrundy数になるみたいだ
つまり、空きポイントを探索して、対応する深さのGrundy数を計算 & xorをとればよい

あとは実装だ
prefix treeを作りたいな？
適当にノードを追加して作ってあげればいいか
保持するデータは、0の子index,1の子index,深さがあれば十分かな
計算量がちょっと気になるところ(再帰はなるべく避けたいところ)

"""

def tp_z(now , nd):

    if z_child[now] == None:
        ret = len(z_child)
        z_child[now] = ret
        z_child.append(None)
        o_child.append(None)
        dep.append(nd + 1)
    else:
        ret = z_child[now]

    return ret

def tp_o(now , nd):

    if o_child[now] == None:
        ret = len(z_child)
        o_child[now] = ret
        z_child.append(None)
        o_child.append(None)
        dep.append(nd + 1)
    else:
        ret = o_child[now]

    return ret

def grundy(nd):

    if nd == 0:
        return 0

    for i in range(64):
        if nd % (2**i) != 0:
            return 2**(i-1)
        

N,L = map(int,input().split())

z_child = [None]
o_child = [None]
dep = [0]

for loop in range(N):

    now = 0
    s = input()

    for i in s:
        if i == "0":
            now = tp_z(now , dep[now])
        else:
            now = tp_o(now , dep[now])

#print (z_child)
#print (o_child)
#print (dep)

ans = 0
for i in range(len(z_child)):

    if z_child[i] == None:
        ans ^= grundy(L - dep[i])
    if o_child[i] == None:
        ans ^= grundy(L - dep[i])

if ans == 0:
    print ("Bob")
else:
    print ("Alice")

import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**6)
from collections import deque

N,M = map(int,input().split())
words = [deque(input().rstrip()) for _ in range(N)]

# 深さLのfull treeの小さい方を計算してみる
def G_small():
    G = [0] * 100
    G_cum = [0] * 100
    G[0] = 1
    G_cum[0] = 1
    for n in range(1,100):
        can_make = set(G_cum[n-1]^G_cum[x] for x in range(n)) | set([G_cum[n-1]])
        rest = set(range(n+10)) - can_make
        G[n] = min(rest)
        G_cum[n] = G_cum[n-1] ^ G[n]
    return G

# print([(n,G[n]) for n in range(100)])
# G[n] = 2**ord_2(n+1)
# 値がわかれば証明は易しい

def G(n):
    n += 1
    x = 1
    while not n & 1:
        n >>= 1
        x <<= 1
    return x

def calc_grandy(words,L):
    if not words:
        return G(L)
    if len(words) == 1 and words[0] == deque():
        return 0
    words_0 = []
    words_1 = []
    for word in words:
        first = word.popleft()
        if first == '0':
            words_0.append(word)
        else:
            words_1.append(word)
    return calc_grandy(words_0,L-1) ^ calc_grandy(words_1,L-1)

g = calc_grandy(words,M)

answer = 'Alice' if g != 0 else 'Bob'
print(answer)

import sys
sys.setrecursionlimit(1000000)

def getGrundyNumber(x):
    ans = 1
    while x % (ans * 2) == 0:
        ans *= 2
    return ans


def dfs(iT, Hgt):
    num = 0
    for c in Trie[iT]:
        if c != -1:
            dfs(c, Hgt - 1)
            num += 1

    if num == 1:
        Hgts[Hgt - 1] = Hgts.get(Hgt - 1, 0) + 1


N, L = list(map(int, input().split()))
Ss = [input() for i in range(N)]

# トライ木を作成する
Trie = [[-1, -1]]
for S in Ss:
    iT = 0
    for c in map(int, S):
        if Trie[iT][c] == -1:
            Trie += [[-1, -1]]
            Trie[iT][c] = len(Trie) - 1
        iT = Trie[iT][c]

# 子が１つの頂点を探す
Hgts = {}
dfs(0, L + 1)

# Grundy数のXORを求める
ans = 0
for Hgt, num in list(Hgts.items()):
    if num % 2:
        ans ^= getGrundyNumber(Hgt)

if ans:
    print('Alice')
else:
    print('Bob')


def main():
    import sys
    from collections import defaultdict
    input = sys.stdin.readline

    mod = 10**10+7
    mod2 = 10**10+9
    mod3 = 998244353
    N, L = list(map(int, input().split()))
    dic = defaultdict(int)
    dic2 = defaultdict(int)
    dic3 = defaultdict(int)
    h_list = []
    h2_list = []
    h3_list = []
    pair = {}
    pair2 = {}
    pair3 = {}
    M = 0
    for _ in range(N):
        s = input().rstrip('\n')
        h = 0
        h2 = 0
        h3 = 0
        for i in range(len(s)):
            M += 1
            h = (h*1007 + int(s[i]) + 1) % mod
            pair[h] = (h + 1)%mod if s[i] == '0' else (h-1)%mod
            h2 = (h2 * 2009 + int(s[i]) + 1) % mod2
            pair2[h2] = (h2 + 1)%mod2 if s[i] == '0' else (h2-1)%mod2
            h3 = (h3 * 3001 + int(s[i]) + 1) % mod3
            pair3[h3] = (h3 + 1) % mod3 if s[i] == '0' else (h3 - 1) % mod3
            dic[h] = i+1
            dic2[h2] = i+1
            dic[h3] = i+1
            h_list.append(h)
            h2_list.append(h2)
            h3_list.append(h3)

    g = 0
    seen = defaultdict(int)
    seen2 = defaultdict(int)
    seen3 = defaultdict(int)
    for i in range(M):
        s, s2, s3 = h_list[i], h2_list[i], h3_list[i]
        if seen[s] and seen2[s2] and seen3[s3]:
            continue
        t = pair[s]
        t2 = pair2[s2]
        t3 = pair3[s3]
        if dic[t] == 0 or dic2[t2] == 0 or dic3[t3] == 0:
            p = [dic[s], dic2[s2], dic3[s3]]
            p.sort()
            tmp = L - p[1] + 1
            '''
            if not seen[s]:
                tmp = L - dic[s] + 1
            elif not seen[s2]:
                tmp = L - dic2[s2] + 1
            else:
                tmp = L - dic3[s3] + 1
            '''
            cnt = 0
            while tmp % 2 == 0:
                tmp //= 2
                cnt += 1
            g ^= (2**cnt)
            #print(g, s, s2, t, t2, dic[t], dic2[t2])
        seen[s] = 1
        seen2[s2] = 1
        seen3[s3] = 1

    if g:
        print('Alice')
    else:
        print('Bob')


def __starting_point():
    main()

__starting_point()

from collections import defaultdict


def solve(l, ss):
    xor = 0
    for d in range(min(ss), l + 1):
        sl = ss[d]
        sl.sort()
        while sl:
            s = sl.pop()
            ps = s[:-1]
            ss[d + 1].append(ps)
            if s[-1] == '1' and sl and sl[-1][:-1] == ps:
                sl.pop()
            else:
                xor ^= d & -d
        del ss[d]
    return xor


n, l = list(map(int, input().split()))
ss = defaultdict(list)
for s in (input() for _ in range(n)):
    ss[l - len(s) + 1].append(s)

print(('Alice' if solve(l, ss) else 'Bob'))


# coding: utf-8
# Your code here!
import sys
readline = sys.stdin.readline
read = sys.stdin.read

_,L = list(map(int,readline().split()))
s = read().split()

def grundy(x):
    if x==0: return 0
    r = 1
    while x%2==0:
        x //= 2
        r *= 2
    return r

g = 0
st = [(s,0)]
while st:
    a,idx = st.pop()
    a0 = []
    a1 = []
    for i in a:
        if len(i) <= idx:
            continue
        if i[idx] == "0":
            a0.append(i)
        else:
            a1.append(i)
    
    if (a0 and not a1) or (a1 and not a0):
        g ^= grundy(L-idx)

    if a0: st.append((a0,idx+1))
    if a1: st.append((a1,idx+1))

if g: print("Alice")
else: print("Bob")



import sys
readline = sys.stdin.readline

class Node:
    def __init__(self, sigma, depth):
        self.end = False
        self.child = [None] * sigma
        self.depth = depth
    def __setitem__(self, i, x):
        self.child[i] = x
    def __getitem__(self, i):
        return self.child[i]

class Trie():
    def __init__(self, sigma):
        self.sigma = sigma
        self.root = Node(sigma, 0)
    
    def add(self, S):
        vn = self.root
        for cs in S:
            if vn[cs] is None:
                vn[cs] = Node(self.sigma, vn.depth + 1)
            vn = vn[cs]
        vn.end = True

ans = 0

N, L = list(map(int, readline().split()))

Tr = Trie(2)

for _ in range(N):
    S = list(map(int, readline().strip()))
    Tr.add(S)

cnt = 0
stack = [Tr.root]
while stack:
    vn = stack.pop()
    for i in range(2):
        if vn[i] is None:
            r = L - vn.depth
            cnt ^= -r&r
        else:
            stack.append(vn[i])

print(('Alice' if cnt else 'Bob'))


N, L = map(int, input().split())

make = lambda:[None, None, 0]
root = make()
def construct(s):
    n = root
    for i in s:
        if n[i] is None:
            n[i] = n = make()
        else:
            n = n[i]
    n[2] = 1

for i in range(N):
    s = map(int, input())
    construct(s)

caps = {}
st = [(root, 0, 0)]
while st:
    n, i, l = st.pop()
    if i:
        if n[1] is None:
            caps[L - l] = caps.get(L - l, 0) + 1
        else:
            if not n[1][2]:
                st.append((n[1], 0, l+1))
    else:
        st.append((n, 1, l))
        if n[0] is None:
            caps[L - l] = caps.get(L - l, 0) + 1
        else:
            if not n[0][2]:
                st.append((n[0], 0, l+1))
ans = 0
for v in caps:
    k = caps[v]
    if k % 2 == 0:
        continue
    v -= 1
    r = 1
    while v % 4 == 3:
        v //= 4
        r *= 4
    if v % 4 == 1:
        ans ^= r * 2
    else:
        ans ^= r
print('Alice' if ans else 'Bob')

import sys
#from collections import defaultdict
sys.setrecursionlimit(10**6)

def input():
	return sys.stdin.readline()[:-1]
n, l = list(map(int, input().split()))
ss = [list(input())[::-1] for _ in range(n)]

def addTree(tree, sentence):
	if not sentence:
		return None   

	if sentence[-1] not in tree:
		tree[sentence[-1]] = {}

	p = sentence.pop()
	tree[p] = addTree(tree[p], sentence)

	return tree

def createTree(sentences):    
	tree = {}
	for sentence in sentences:
		tree = addTree(tree, sentence)

	return tree

tree = createTree(ss)
#print(tree)

grundy = 0
def dfs(cur, level):
	nonlocal grundy
	if cur is None:
		return
	elif len(cur) == 1:
		grundy ^= (l-level) & (-l+level)
	for k in list(cur.keys()):
		dfs(cur[k], level+1)
	return
	
dfs(tree, 0)

if grundy == 0:
	print("Bob")
else:
	print("Alice")


import random
N, L = list(map(int,input().split()))
M = 2*10**5+1
sets = [set() for _ in range(M+1)]

MOD = 282525872416376072492401
r_max = 10**6
inv_t = [0,1]
for i in range(2,r_max+1):
  #print(MOD % i)
  inv_t.append(inv_t[MOD % i] * (MOD - MOD // i) % MOD)
r = random.randint(10**5, r_max)
rinv = inv_t[r]
#print(r, rinv)

def has(s):
  ans = 0
  i = 0
  for x in s:
    ans *= r
    ans += int(x) + (i+1)**2
    ans %= MOD
    i += 1
  return ans % MOD
def tract(x, s, l):
  return ((x - int(s) - l**2) * rinv ) %MOD
 
strs = []
dic = {}
ct = 0
for i in range(N):
  s = input()
  strs.append(s)
  sets[len(s)].add(has(s))
  if has(s) in dic:
    print(("warning!!", s))
    ct += 1
  dic[has(s)] = i
#print(ct)
#print(sets[0:3])
#print(dic)
free_num = [0 for _ in range(M+1)]
for l in range(M,0,-1):
  while len(sets[l]) > 0:
    x = sets[l].pop()
    #print(x, dic[x], strs[dic[x]], l)
    #if l-1 > len(strs[dic[x]]):
      #print(l-1, x, dic[x], strs[dic[x]], sets[l])
    last = strs[dic[x]][l-1]
    x_p = tract(x, last, l)
    sets[l-1].add(x_p) 
    dic[x_p] = dic[x]
    x_anti = (x_p*r + 1-int(last) + l**2) % MOD
    if x_anti in sets[l]:
      sets[l].remove(x_anti)
    else:
      free_num[l] += 1
    #print(l, x, free_num[0:2],sets[0:3], x_p, x_anti, last)
#print(free_num[0:100])
Grandy = 0
for i in range(M+1):
  G_base = L - i + 1
  if free_num[i] % 2 == 1:
    #print(i, G_base)
    Grandy ^= (G_base & -G_base)
#print(Grandy)    
print(("Alice" if Grandy > 0 else "Bob"))
  
#print(2**54 & -2**54, 2**54) 
  


import sys

sys.setrecursionlimit(100000)


def dfs(cur, dep=0):
    if cur == -1:
        x = l - dep
        return x & -x
    return dfs(trie[cur][0], dep + 1) ^ dfs(trie[cur][1], dep + 1)


n, l = list(map(int, input().split()))
trie = [[-1, -1] for _ in range(100001)]
idx = 1
for s in (input() for _ in range(n)):
    cur = 0
    for c in map(int, s):
        if trie[cur][c] == -1:
            trie[cur][c] = idx
            idx += 1
        cur = trie[cur][c]

xor = dfs(trie[0][0]) ^ dfs(trie[0][1])
print(('Alice' if xor else 'Bob'))


import sys
readline = sys.stdin.readline

class Node:
    def __init__(self, sigma, depth):
        self.end = False
        self.child = [None] * sigma
        self.depth = depth
    def __setitem__(self, i, x):
        self.child[i] = x
    def __getitem__(self, i):
        return self.child[i]

sigma = 2
root = Node(sigma, 0)
def add_trie(S):
    vn = root
    for cs in S:
        if vn[cs] is None:
            vn[cs] = Node(sigma, vn.depth + 1)
        vn = vn[cs]
    vn.end = True

ans = 0

N, L = map(int, readline().split())

for _ in range(N):
    S = list(map(int, readline().strip()))
    add_trie(S)

cnt = 0
stack = [root]
while stack:
    vn = stack.pop()
    for i in range(2):
        if vn[i] is None:
            r = L - vn.depth
            cnt ^= -r&r
        else:
            stack.append(vn[i])

print('Alice' if cnt else 'Bob')

def getGrundyNumber(x):
    ans = 1
    while x % (ans * 2) == 0:
        ans *= 2
    return ans


N, L = list(map(int, input().split()))
Ss = [input() for i in range(N)]

Ss.sort()

Hgts = {L: 2}
prev = '_'
for S in Ss:
    for iS, (a, b) in enumerate(zip(prev, S)):
        if a != b:
            Hgts[L - iS] -= 1
            for h in range(L - len(S) + 1, L - iS):
                Hgts[h] = Hgts.get(h, 0) + 1
            break
    prev = S

ans = 0
for Hgt, num in list(Hgts.items()):
    if num % 2:
        ans ^= getGrundyNumber(Hgt)

if ans:
    print('Alice')
else:
    print('Bob')


def main():
    N,L = map(int,input().split())
    keys = []
    grundy_num=0
    if N==1:
        print("Alice")
        return
    for i in range(N):
        s = input()
        #merge node:
        while len(s)>0:
            if s[-1]=='1' and s[:-1]+'0' in keys:
                keys.remove(s[:-1]+'0')
                s = s[:-1]
            elif s[-1]=='0' and s[:-1]+'1' in keys:
                keys.remove(s[:-1]+'1')
                s = s[:-1]
            else:
                if s!="":
                    keys.append(s)
                break
    if len(keys):
        keys = sorted(keys,key=len,reverse=True)
        grundy_list = []
        while len(keys):
            j = keys[0]
            keys.remove(j)
            temp_s = list(reversed(bin(L-len(j)+1)))
            grundy_list.append(1 << temp_s.index('1'))
            if j[:-1]!="":
                keys.append(j[:-1])
            j = j[:-1]
            #merge node
            while len(j)>0:
                if j[-1]=='1' and j[:-1]+'0' in keys:
                    keys.remove(j[:-1]+'0')
                    keys.remove(j)
                    j = j[:-1]
                    if j!="":
                        keys.append(j)
                elif j[-1]=='0' and j[:-1]+'1' in keys:
                    keys.remove(j[:-1]+'1')
                    keys.remove(j)
                    j = j[:-1]
                    if j!="":
                        keys.append(j)
                else:
                    break
            keys = sorted(keys,key=len,reverse=True)
        grundy_num = grundy_list[0]
        for i in range(1,len(grundy_list)):
            grundy_num = grundy_num^grundy_list[i]
    print("Bob" if grundy_num==0 else "Alice")


def __starting_point():
    main()
__starting_point()

from collections import defaultdict
import sys

sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline
int1 = lambda x: int(x) - 1
p2D = lambda x: print(*x, sep="\n")

def main():
    n, l = map(int, input().split())
    ss = [input()[:-1] for _ in range(n)] + ["$"]
    ss.sort()
    # print(ss)

    # 隣同士のsを先頭から比べて、異なるところでそのlevelの木を消して、それ以降の木を追加する
    cnt_tree = defaultdict(int)
    cnt_tree[l] = 2
    for s0, s1 in zip(ss, ss[1:]):
        for depth, (c0, c1) in enumerate(zip(s0, s1)):
            if c0 == c1: continue
            cnt_tree[l - depth] -= 1
            for lv in range(l - depth - 1, l - len(s1), -1):
                cnt_tree[lv] += 1
            # print(cnt_tree)
            break

    # Grundy数をlv & -lvで求め、xorをとる
    sum_xor = 0
    for lv, cnt in cnt_tree.items():
        if cnt % 2 == 0: continue
        sum_xor ^= lv & -lv

    if sum_xor:
        print("Alice")
    else:
        print("Bob")

main()


from functools import cmp_to_key


def f(s, t):
    m = min(len(s), len(t))
    if s[:m] > t[:m]:
        return 1
    elif s[:m] < t[:m]:
        return -1
    else:
        if len(s) > len(t):
            return -1
        else:
            return 1


def ms(s, t):
    i = 0
    for c1, c2 in zip(s, t):
        if c1 != c2:
            return i
        i += 1
    return i


def xxor(x):
    if x & 3 == 0:
        return x
    if x & 3 == 1:
        return 1
    if x & 3 == 2:
        return x + 1
    if x & 3 == 3:
        return 0


def gray(x):
    return x ^ (x // 2)


n, l = list(map(int, input().split()))
s = [input() for _ in range(n)]
s.sort(key=cmp_to_key(f))
g = gray(l) ^ gray(l - len(s[0]))
for i in range(n - 1):
    b = ms(s[i], s[i + 1]) + 1
    g ^= gray(l - b + 1) ^ gray(l - b)
    t = len(s[i + 1]) - b
    g ^= gray(l - b) ^ gray(l - b - t)
if g == 0:
    print('Bob')
else:
    print('Alice')


def main():
    import sys
    input = sys.stdin.readline

    class TreiNode:
        def __init__(self, char_num, depth):
            self.end = False
            self.child = [None] * char_num
            self.depth = depth

        def __setitem__(self, i, x):
            self.child[i] = x

        def __getitem__(self, i):
            return self.child[i]

    class Trei:
        def __init__(self, char_num):
            self.root = TreiNode(char_num, 0)
            self.char_num = char_num

        def add(self, S):
            v = self.root
            for s in S:
                if v[s] is None:
                    v[s] = TreiNode(self.char_num, v.depth + 1)
                v = v[s]
            v.end = True

        def exist(self, S):
            v = self.root
            for s in S:
                if v[s] is None:
                    return False
                v = v[s]
            if v.end:
                return True
            else:
                return False

    N, L = list(map(int, input().split()))
    T = Trei(2)
    for _ in range(N):
        S = input().rstrip('\n')
        S = [int(s) for s in S]
        T.add(S)

    g = 0
    st = [T.root]
    while st:
        v = st.pop()
        for i in range(2):
            if v[i] is None:
                d = L - v.depth
                g ^= d & -d
            else:
                st.append(v[i])
    if g:
        print('Alice')
    else:
        print('Bob')


def __starting_point():
    main()

__starting_point()

import sys
input=sys.stdin.readline
sys.setrecursionlimit(10**9)
from collections import deque

class Node:
    def __init__(self,depth):
        self.depth=depth
        self.left=None
        self.right=None

def insert(node,s):
    n=node
    for i in range(len(s)):
        t=s[i]
        if t=='0':
            if n.left is None:
                n.left=Node(i+1)
            n=n.left
        else:
            if n.right is None:
                n.right=Node(i+1)
            n=n.right

class Trie:
    def __init__(self):
        self.root=Node(0)
    def insert(self,s:str):
        insert(self.root,s)

n,l=map(int,input().split())
S=[input().strip() for _ in range(n)]
trie=Trie()
for s in S:
    trie.insert(s)
Data=[]
q=deque([trie.root])

def dfs(node):
    if node.right is None and node.left is None:
        return
    if node.right is None or node.left is None:
        Data.append(l-node.depth)
    if node.right:
        q.append(node.right)
    if node.left:
        q.append(node.left)

while q:
    dfs(q.popleft())
xor=0

def Grundy(n):
    ret=1
    while n%2==0:
        n//=2
        ret*=2
    return ret

for i in Data:
    xor^=Grundy(i)
print('Alice' if xor else 'Bob')

import sys
from collections import deque

sys.setrecursionlimit(10000)
INF = float('inf')

N, L = list(map(int, input().split()))
S = set()
for _ in range(N):
    S.add(input())


# 追加できるのが 01... だけで
# L が 4 とすると
# 01: 次はない
# 010: 011 がある
# 011: 010 がある
# 0100: 011, 0101 がある
# 0101: 011, 0100
# 0110: 010, 0111
# 0111: 010, 0110
# 候補の長さと L だけによって次の候補の長さと数が決まる
def grundy(size):
    """
    :param int size: 候補の長さ
    :return:
    """
    # gs = {0}
    # g = 0
    # for sz in range(size + 1, L + 1):
    #     g ^= grundy(sz)
    #     gs.add(g)
    #
    # i = 0
    # while i in gs:
    #     i += 1
    # return i
    i = 1
    while (L - size + 1) % i == 0:
        i *= 2
    return i // 2


# S の各要素からトライ木つくる
trie = {}
for s in S:
    t = trie
    for c in s:
        if c not in t:
            t[c] = {}
        t = t[c]

# 候補文字列の長さのリスト
ok_size_list = []
children = deque()
children.append((trie, 0))
while len(children) > 0:
    node, size = children.popleft()
    # 0 か 1 か片方しか行けなかったら、行けない方は S の prefix にならない
    # 両方行けない場合はどう追加しても S のいずれかが prefix になってしまうからダメ
    if len(node) == 1:
        ok_size_list.append(size + 1)
    for c, child in node.items():
        children.append((child, size + 1))

g = 0
for size in ok_size_list:
    g ^= grundy(size)
if g != 0:
    print('Alice')
else:
    print('Bob')