{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h1>238. Product of Array Except Self</h1>\n",
    "<hr>\n",
    "\n",
    "<!--Copy Paste Leetcode statement between-->\n",
    "<p>Given an array <code>nums</code> of <em>n</em> integers where <em>n</em> &gt; 1, &nbsp;return an array <code>output</code> such that <code>output[i]</code> is equal to the product of all the elements of <code>nums</code> except <code>nums[i]</code>.</p>\n",
    "\n",
    "<p><b>Example:</b></p>\n",
    "\n",
    "<pre><b>Input:</b>  <code>[1,2,3,4]</code>\n",
    "<b>Output:</b> <code>[24,12,8,6]</code>\n",
    "</pre>\n",
    "\n",
    "<p><strong>Constraint:</strong>&nbsp;It's guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer.</p>\n",
    "\n",
    "<p><strong>Note: </strong>Please solve it <strong>without division</strong> and in O(<em>n</em>).</p>\n",
    "<!--Copy Paste Leetcode statement between-->\n",
    "\n",
    "<p>&nbsp;</p>\n",
    "<a href=\"https://leetcode.com/problems/product-of-array-except-self/\">Source</a> \n",
    "<hr>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h4>Code</h4>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def product_except_self(nums):\n",
    "    \"\"\"Using the following trick:\n",
    "\n",
    "    nums =   [a,      b,      c,      d,      e   ]\n",
    "    -----------------------------------------------\n",
    "    L =      [1,      a,      ab,     abc,    abcd]\n",
    "    R =      [bcde,   cde,    de,     e,      1   ]\n",
    "    -----------------------------------------------\n",
    "    result = [bcde,   acde,   abde,   abce,   abcd]\n",
    "    \n",
    "    Time Complexity:  O(n)\n",
    "    Space Complexity: O(n)\n",
    "    \"\"\"\n",
    "    n = len(nums)\n",
    "    L, R, output = ([1]*n for _ in range(3))\n",
    "    # L[i] / R[i] = product of all the elements strictly to the left / right of i\n",
    "\n",
    "    for i in range(1, n):\n",
    "        L[i] = L[i-1]*nums[i-1]\n",
    "        R[n-1-i] = R[n-i]*nums[n-i]\n",
    "\n",
    "    for i in range(n):\n",
    "        output[i] = L[i]*R[i]\n",
    "    return output"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h4>Check</h4>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[24, 12, 8, 6]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nums = [1,2,3,4]\n",
    "product_except_self(nums)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<hr>\n",
    "<h4>Follow up:</h4>\n",
    "<p>Could you solve it with constant space complexity? (The output array <strong>does not</strong> count as extra space for the purpose of space complexity analysis.)</p>\n",
    "\n",
    "<h4>Code</h4>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def product_except_self(nums):\n",
    "    \"\"\"Solution with O(1) memory (2 passes).\"\"\"\n",
    "    n = len(nums)\n",
    "    output = [1]*n  # output = L\n",
    "    R = 1           # we update R and build output along instead of using list\n",
    "    for i in range(1, n):\n",
    "        output[i] = output[i-1]*nums[i-1]\n",
    "    for i in range(n-1, -1, -1):\n",
    "        output[i] *= R\n",
    "        R *= nums[i]\n",
    "    return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def product_except_self(nums):\n",
    "    \"\"\"Solution with O(1) memory (1 pass).\"\"\"\n",
    "    n = len(nums)\n",
    "    output = [1] * n\n",
    "    L = R = 1\n",
    "    for i in range(n):\n",
    "        output[i] *= L\n",
    "        output[~i] *= R  # Reminder ~i = -1-i\n",
    "        L *= nums[i]\n",
    "        R *= nums[~i]\n",
    "    return output"
   ]
  }
 ],
 "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.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}