Got the solutions working
[ou-summer-of-code-2017.git] / 09-resolving-the-bill / subsequence.ipynb
diff --git a/09-resolving-the-bill/subsequence.ipynb b/09-resolving-the-bill/subsequence.ipynb
new file mode 100644 (file)
index 0000000..c945df8
--- /dev/null
@@ -0,0 +1,573 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Subsequence strings\n",
+    "\n",
+    "Given two strings `a` and `b`, is `a` a subsequence of `b`?\n",
+    "\n",
+    "For example,\n",
+    "Given:\n",
+    "a = \"aabcc\",\n",
+    "b = \"dabaabcacb\",\n",
+    "\n",
+    "return True : dAbAaBCaCb\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 54,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "s1 = \"aabcc\"\n",
+    "s2t = \"dabaabcacb\"\n",
+    "s2f = \"dabacc\"\n",
+    "\n",
+    "s2 = s2t"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`dp_table[i, j]` is True if first `i` characters of `s1` can be found in first `j` characters of `s2`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{(0, 0): False,\n",
+       " (0, 1): False,\n",
+       " (0, 2): False,\n",
+       " (0, 3): False,\n",
+       " (0, 4): False,\n",
+       " (0, 5): False,\n",
+       " (0, 6): False,\n",
+       " (0, 7): False,\n",
+       " (0, 8): False,\n",
+       " (0, 9): False,\n",
+       " (0, 10): False,\n",
+       " (1, 0): False,\n",
+       " (1, 1): False,\n",
+       " (1, 2): False,\n",
+       " (1, 3): False,\n",
+       " (1, 4): False,\n",
+       " (1, 5): False,\n",
+       " (1, 6): False,\n",
+       " (1, 7): False,\n",
+       " (1, 8): False,\n",
+       " (1, 9): False,\n",
+       " (1, 10): False,\n",
+       " (2, 0): False,\n",
+       " (2, 1): False,\n",
+       " (2, 2): False,\n",
+       " (2, 3): False,\n",
+       " (2, 4): False,\n",
+       " (2, 5): False,\n",
+       " (2, 6): False,\n",
+       " (2, 7): False,\n",
+       " (2, 8): False,\n",
+       " (2, 9): False,\n",
+       " (2, 10): False,\n",
+       " (3, 0): False,\n",
+       " (3, 1): False,\n",
+       " (3, 2): False,\n",
+       " (3, 3): False,\n",
+       " (3, 4): False,\n",
+       " (3, 5): False,\n",
+       " (3, 6): False,\n",
+       " (3, 7): False,\n",
+       " (3, 8): False,\n",
+       " (3, 9): False,\n",
+       " (3, 10): False,\n",
+       " (4, 0): False,\n",
+       " (4, 1): False,\n",
+       " (4, 2): False,\n",
+       " (4, 3): False,\n",
+       " (4, 4): False,\n",
+       " (4, 5): False,\n",
+       " (4, 6): False,\n",
+       " (4, 7): False,\n",
+       " (4, 8): False,\n",
+       " (4, 9): False,\n",
+       " (4, 10): False,\n",
+       " (5, 0): False,\n",
+       " (5, 1): False,\n",
+       " (5, 2): False,\n",
+       " (5, 3): False,\n",
+       " (5, 4): False,\n",
+       " (5, 5): False,\n",
+       " (5, 6): False,\n",
+       " (5, 7): False,\n",
+       " (5, 8): False,\n",
+       " (5, 9): False,\n",
+       " (5, 10): False}"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "dp_table = {(i, j): False\n",
+    "           for i in range(len(s1)+1)\n",
+    "           for j in range(len(s2)+1)}\n",
+    "dp_table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def show_table(table):\n",
+    "    return '\\n'.join(\n",
+    "        ' '.join(str(table[i, j])[0] for j in sorted(set([k[1] for k in table])))\n",
+    "        for i in sorted(set([k[0] for k in table])))        "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "F F F F F F F F F F F\n",
+      "F F F F F F F F F F F\n",
+      "F F F F F F F F F F F\n",
+      "F F F F F F F F F F F\n",
+      "F F F F F F F F F F F\n",
+      "F F F F F F F F F F F\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(show_table(dp_table))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 47,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "aaaa aaababb\n",
+      "aa 0 0 ! ! True\n",
+      "aa 0 1 ! ! True\n",
+      "aa 0 2 ! ! True\n",
+      "aa 0 3 ! ! True\n",
+      "aa 0 4 ! ! True\n",
+      "aa 0 5 ! ! True\n",
+      "aa 0 6 ! ! True\n",
+      "aa 0 7 ! ! True\n",
+      "s1 1 1 a a True\n",
+      "s2 1 2 a a True\n",
+      "s1 1 2 a a True\n",
+      "s2 1 3 a a True\n",
+      "s1 1 3 a a True\n",
+      "s2 1 4 a b True\n",
+      "s2 1 5 a a True\n",
+      "s1 1 5 a a True\n",
+      "s2 1 6 a b True\n",
+      "s2 1 7 a b True\n",
+      "s1 2 2 a a True\n",
+      "s2 2 3 a a True\n",
+      "s1 2 3 a a True\n",
+      "s2 2 4 a b True\n",
+      "s2 2 5 a a True\n",
+      "s1 2 5 a a True\n",
+      "s2 2 6 a b True\n",
+      "s2 2 7 a b True\n",
+      "s1 3 3 a a True\n",
+      "s2 3 4 a b True\n",
+      "s2 3 5 a a True\n",
+      "s1 3 5 a a True\n",
+      "s2 3 6 a b True\n",
+      "s2 3 7 a b True\n",
+      "xx 4 4 a b False\n",
+      "s1 4 5 a a True\n",
+      "s2 4 6 a b True\n",
+      "s2 4 7 a b True\n",
+      "T T T T T T T T\n",
+      "F T T T T T T T\n",
+      "F F T T T T T T\n",
+      "F F F T T T T T\n",
+      "F F F F F T T T\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "{(1, 1): (0, 0, 'a', 's1'),\n",
+       " (1, 2): (0, 1, 'a', 's1'),\n",
+       " (1, 3): (0, 2, 'a', 's1'),\n",
+       " (1, 4): (1, 3, 'b', 's2'),\n",
+       " (1, 5): (0, 4, 'a', 's1'),\n",
+       " (1, 6): (1, 5, 'b', 's2'),\n",
+       " (1, 7): (1, 6, 'b', 's2'),\n",
+       " (2, 2): (1, 1, 'a', 's1'),\n",
+       " (2, 3): (1, 2, 'a', 's1'),\n",
+       " (2, 4): (2, 3, 'b', 's2'),\n",
+       " (2, 5): (1, 4, 'a', 's1'),\n",
+       " (2, 6): (2, 5, 'b', 's2'),\n",
+       " (2, 7): (2, 6, 'b', 's2'),\n",
+       " (3, 3): (2, 2, 'a', 's1'),\n",
+       " (3, 4): (3, 3, 'b', 's2'),\n",
+       " (3, 5): (2, 4, 'a', 's1'),\n",
+       " (3, 6): (3, 5, 'b', 's2'),\n",
+       " (3, 7): (3, 6, 'b', 's2'),\n",
+       " (4, 5): (3, 4, 'a', 's1'),\n",
+       " (4, 6): (4, 5, 'b', 's2'),\n",
+       " (4, 7): (4, 6, 'b', 's2')}"
+      ]
+     },
+     "execution_count": 47,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "s2 = s2f\n",
+    "\n",
+    "s1 = 'aaaa'\n",
+    "s2 = 'aaababb'\n",
+    "\n",
+    "print(s1, s2)\n",
+    "\n",
+    "dp_table = {(i, j): False\n",
+    "           for i in range(len(s1)+1)\n",
+    "           for j in range(len(s2)+1)}\n",
+    "\n",
+    "backpointers = {}\n",
+    "\n",
+    "for i in range(len(s1)+1):\n",
+    "    for j in range(i, len(s2)+1):\n",
+    "        if i == 0 or j == 0:\n",
+    "            dp_table[i, j] = True\n",
+    "            print('aa', i, j, '!', '!', dp_table[i, j])\n",
+    "        else:\n",
+    "            # extend by character from s2\n",
+    "            if dp_table[i, j-1]:\n",
+    "                dp_table[i, j] = True\n",
+    "                backpointers[i, j] = (i, j-1, s2[j-1], 's2')\n",
+    "                print('s2', i, j, s1[i-1], s2[j-1], dp_table[i, j])                \n",
+    "            # extend by character from s1\n",
+    "            if dp_table[i-1, j-1] and s1[i-1] == s2[j-1]:\n",
+    "                dp_table[i, j] = True\n",
+    "                backpointers[i, j] = (i-1, j-1, s1[i-1], 's1')                \n",
+    "                print('s1', i, j, s1[i-1], s2[j-1], dp_table[i, j])\n",
+    "            if not dp_table[i, j]:\n",
+    "                print('xx', i, j, s1[i-1], s2[j-1], dp_table[i, j])\n",
+    "\n",
+    "print(show_table(dp_table))\n",
+    "backpointers"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 59,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def show_backtrace(bps):\n",
+    "    i = max([0] + [k[0] for k in bps])\n",
+    "    j = max([0] + [k[1] for k in bps])\n",
+    "    chars = ''\n",
+    "    if (i, j) in bps:\n",
+    "        while i != 0 or j != 0:\n",
+    "            if bps[i, j][3] == 's1':\n",
+    "                chars += bps[i, j][2].upper()\n",
+    "            else:\n",
+    "                chars += bps[i, j][2]\n",
+    "            i, j = bps[i, j][0], bps[i, j][1] \n",
+    "        return ''.join(list(reversed(chars)))\n",
+    "    else:\n",
+    "        return ''"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 60,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'AAAbAbb'"
+      ]
+     },
+     "execution_count": 60,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "show_backtrace(backpointers)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 49,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def is_subseq(seq1, seq2, return_backpointers=False, debug=False):\n",
+    "    \"\"\"Return true if seq1 is a subsequence of seq2.\n",
+    "    If return_backpointers, also return the set of backpointers to\n",
+    "    reconstruct the subsequence\"\"\"\n",
+    "    \n",
+    "    # dp_table[i, j] is True if first i characters of seq1 can\n",
+    "    # be found in the first j characters of seq2\n",
+    "  \n",
+    "    dp_table = {(i, j): False\n",
+    "               for i in range(len(seq1)+1)\n",
+    "               for j in range(len(seq2)+1)}\n",
+    "\n",
+    "    backpointers = {}\n",
+    "    \n",
+    "    for i in range(len(seq1)+1):\n",
+    "        for j in range(i, len(seq2)+1):\n",
+    "            if i == 0 or j == 0:\n",
+    "                dp_table[i, j] = True\n",
+    "                if debug: print('aa', i, j, '!', '!', dp_table[i, j])\n",
+    "            else:\n",
+    "                # extend by character from s2\n",
+    "                if dp_table[i, j-1]:\n",
+    "                    dp_table[i, j] = True\n",
+    "                    backpointers[i, j] = (i, j-1, seq2[j-1], 's2')\n",
+    "                    if debug: print('s2', i, j, seq1[i-1], seq2[j-1], dp_table[i, j])                \n",
+    "                # extend by character from s1\n",
+    "                if dp_table[i-1, j-1] and seq1[i-1] == seq2[j-1]:\n",
+    "                    dp_table[i, j] = True\n",
+    "                    backpointers[i, j] = (i-1, j-1, seq1[i-1], 's1')                \n",
+    "                    if debug: print('s1', i, j, seq1[i-1], seq2[j-1], dp_table[i, j])\n",
+    "                if not dp_table[i, j]:\n",
+    "                    if debug: print('xx', i, j, seq1[i-1], seq2[j-1], dp_table[i, j]) \n",
+    "   \n",
+    "    if return_backpointers:\n",
+    "        return dp_table[len(seq1), len(seq2)], backpointers\n",
+    "    else:\n",
+    "        return dp_table[len(seq1), len(seq2)]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 39,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "is_subseq(s1, s2t)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 55,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 55,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "tf, bps = is_subseq(s1, s2t, return_backpointers=True)\n",
+    "tf"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 56,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'AABCaCb'"
+      ]
+     },
+     "execution_count": 56,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "show_backtrace(bps)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 42,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "is_subseq(s1, s2f)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "aa 0 0 ! ! True\n",
+      "aa 0 1 ! ! True\n",
+      "aa 0 2 ! ! True\n",
+      "aa 0 3 ! ! True\n",
+      "aa 0 4 ! ! True\n",
+      "aa 0 5 ! ! True\n",
+      "aa 0 6 ! ! True\n",
+      "aa 0 7 ! ! True\n",
+      "s1 1 1 a a True\n",
+      "s2 1 2 a a True\n",
+      "s1 1 2 a a True\n",
+      "s2 1 3 a a True\n",
+      "s1 1 3 a a True\n",
+      "s2 1 4 a b True\n",
+      "s2 1 5 a a True\n",
+      "s1 1 5 a a True\n",
+      "s2 1 6 a b True\n",
+      "s2 1 7 a b True\n",
+      "s1 2 2 a a True\n",
+      "s2 2 3 a a True\n",
+      "s1 2 3 a a True\n",
+      "s2 2 4 a b True\n",
+      "s2 2 5 a a True\n",
+      "s1 2 5 a a True\n",
+      "s2 2 6 a b True\n",
+      "s2 2 7 a b True\n",
+      "s1 3 3 a a True\n",
+      "s2 3 4 a b True\n",
+      "s2 3 5 a a True\n",
+      "s1 3 5 a a True\n",
+      "s2 3 6 a b True\n",
+      "s2 3 7 a b True\n",
+      "xx 4 4 a b False\n",
+      "s1 4 5 a a True\n",
+      "s2 4 6 a b True\n",
+      "s2 4 7 a b True\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(True,\n",
+       " {(1, 1): (0, 1, 'a', 's1'),\n",
+       "  (1, 2): (0, 2, 'a', 's1'),\n",
+       "  (1, 3): (0, 3, 'a', 's1'),\n",
+       "  (1, 4): (1, 3, 'b', 's2'),\n",
+       "  (1, 5): (0, 5, 'a', 's1'),\n",
+       "  (1, 6): (1, 5, 'b', 's2'),\n",
+       "  (1, 7): (1, 6, 'b', 's2'),\n",
+       "  (2, 2): (1, 2, 'a', 's1'),\n",
+       "  (2, 3): (1, 3, 'a', 's1'),\n",
+       "  (2, 4): (2, 3, 'b', 's2'),\n",
+       "  (2, 5): (1, 5, 'a', 's1'),\n",
+       "  (2, 6): (2, 5, 'b', 's2'),\n",
+       "  (2, 7): (2, 6, 'b', 's2'),\n",
+       "  (3, 3): (2, 3, 'a', 's1'),\n",
+       "  (3, 4): (3, 3, 'b', 's2'),\n",
+       "  (3, 5): (2, 5, 'a', 's1'),\n",
+       "  (3, 6): (3, 5, 'b', 's2'),\n",
+       "  (3, 7): (3, 6, 'b', 's2'),\n",
+       "  (4, 5): (3, 5, 'a', 's1'),\n",
+       "  (4, 6): (4, 5, 'b', 's2'),\n",
+       "  (4, 7): (4, 6, 'b', 's2')})"
+      ]
+     },
+     "execution_count": 43,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "is_subseq('aaaa', 'aaababb', return_backpointers=True, debug=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.5.2+"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}