Grammar denotes the syntactical rules for conversation in natural language. But in the theory of formal language, grammar is defined as a set of rules that can generate strings. The set of all strings that can be generated from a grammar is called the language of the grammar.
Context Free Grammar: 
We are given a Context Free Grammar G = (V, X, R, S) and a string w, where:
- V is a finite set of variables or non-terminal symbols,
- X is a finite set of terminal symbols,
- R is a finite set of rules,
- S is the start symbol, a distinct element of V, and
- V and X are assumed to be disjoint sets.
The Membership problem is defined as: Grammar G generates a language L(G). Is the given string a member of L(G)?
Chomsky Normal Form: 
A Context Free Grammar G is in Chomsky Normal Form (CNF) if each rule if each rule of G is of the form:
- A –> BC, [ with at most two non-terminal symbols on the RHS ]
- A –> a, or [ one terminal symbol on the RHS ]
- S –> nullstring, [ null string ]
Cocke-Younger-Kasami Algorithm 
It is used to solves the membership problem using a dynamic programming approach. The algorithm is based on the principle that the solution to problem [i, j] can constructed from solution to subproblem [i, k] and solution to sub problem [k, j]. The algorithm requires the Grammar G to be in Chomsky Normal Form (CNF). Note that any Context-Free Grammar can be systematically converted to CNF. This restriction is employed so that each problem can only be divided into two subproblems and not more – to bound the time complexity. 
How does the CYK Algorithm work?
For a string of length N, construct a table T of size N x N. Each cell in the table T[i, j] is the set of all constituents that can produce the substring spanning from position i to j. The process involves filling the table with the solutions to the subproblems encountered in the bottom-up parsing process. Therefore, cells will be filled from left to right and bottom to top.
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| 1 | [1, 1] | [1, 2] | [1, 3] | [1, 4] | [1, 5] | 
| 2 | [2, 2] | [2, 3] | [2, 4] | [2, 5] | |
| 3 | [3, 3] | [3, 4] | [3, 5] | ||
| 4 | [4, 4] | [4, 5] | |||
| 5 | [5, 5] | 
In T[i, j], the row number i denotes the start index and the column number j denotes the end index.
 
The algorithm considers every possible subsequence of letters and adds K to T[i, j] if the sequence of letters starting from i to j can be generated from the non-terminal K. For subsequences of length 2 and greater, it considers every possible partition of the subsequence into two parts, and checks if there is a rule of the form A ? BC in the grammar where B and C can generate the two parts respectively, based on already existing entries in T. The sentence can be produced by the grammar only if the entire string is matched by the start symbol, i.e, if S is a member of T[1, n].
Consider a sample grammar in Chomsky Normal Form:
NP --> Det | Nom Nom --> AP | Nom AP --> Adv | A Det --> a | an Adv --> very | extremely AP --> heavy | orange | tall A --> heavy | orange | tall | muscular Nom --> book | orange | man
Now consider the phrase, “a very heavy orange book“:
a(1) very(2) heavy (3) orange(4) book(5)
Let us start filling up the table from left to right and bottom to top, according to the rules described above:
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| 1 | Det | – | – | NP | NP | 
| 2 | Adv | AP | Nom | Nom | |
| 3 | A, AP | Nom | Nom | ||
| 4 | Nom, A, AP | Nom | |||
| 5 | Nom | 
The table is filled in the following manner:
- T[1, 1] = {Det} as Det –> a is one of the rules of the grammar.
- T[2, 2] = {Adv} as Adv –> very is one of the rules of the grammar.
- T[1, 2] = {} as no matching rule is observed.
- T[3, 3] = {A, AP} as A –> very and AP –> very are rules of the grammar.
- T[2, 3] = {AP} as AP –> Adv (T[2, 2]) A (T[3, 3]) is a rule of the grammar.
- T[1, 3] = {} as no matching rule is observed.
- T[4, 4] = {Nom, A, AP} as Nom –> orange and A –> orange and AP –> orange are rules of the grammar.
- T[3, 4] = {Nom} as Nom –> AP (T[3, 3]) Nom (T[3, 4]) is a rule of the grammar.
- T[2, 4] = {Nom} as Nom –> AP (T[2, 3]) Nom (T[4, 4]) is a rule of the grammar.
- T[1, 4] = {NP} as NP –> Det (T[1, 1]) Nom (T[2, 4]) is a rule of the grammar.
- T[5, 5] = {Nom} as Nom –> book is a rule of the grammar.
- T[4, 5] = {Nom} as Nom –> AP (T[4, 4]) Nom (T[5, 5]) is a rule of the grammar.
- T[3, 5] = {Nom} as Nom –> AP (T[3, 3]) Nom (T[4, 5]) is a rule of the grammar.
- T[2, 5] = {Nom} as Nom –> AP (T[2, 3]) Nom (T[4, 5]) is a rule of the grammar.
- T[1, 5] = {NP} as NP –> Det (T[1, 1]) Nom (T[2, 5]) is a rule of the grammar.
We see that T[1][5] has NP, the start symbol, which means that this phrase is a member of the language of the grammar G.
The parse tree of this phrase would look like this:
Let us look at another example phrase, “a very tall extremely muscular man”:
a(1) very(2) tall(3) extremely(4) muscular(5) man(6)
We will now use the CYK algorithm to find if this string is a member of the grammar G:
| 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|
| 1 | Det | – | – | – | – | NP | 
| 2 | Adv | AP | – | – | Nom | |
| 3 | AP, A | – | – | Nom | ||
| 4 | Adv | AP | Nom | |||
| 5 | A | – | ||||
| 6 | Nom | 
We see that T[1][6] has NP, the start symbol, which means that this phrase is a member of the language of the grammar G.
Below is the implementation of the above algorithm:
 
C++
| // C++ implementation for the// CYK Algorithm#include<bits/stdc++.h>usingnamespacestd;// Non-terminals symbolsvector<string> terminals,non_terminals;// Rules of the grammarunordered_map<string,vector<vector<string>>> R;// function to perform the CYK AlgorithmvoidcykParse(vector<string> w){  intn = (int)w.size();  // Initialize the table  map<int,map<int,set<string>>> T;    // Filling in the table  for(intj=0;j<n;j++)  {    // Iterate over the rules    for(autox:R)    {      string lhs = x.first;      vector<vector<string>> rule = x.second;            for(autorhs:rule)      {        // If a terminal is found        if(rhs.size() == 1 && rhs[0] == w[j])          T[j][j].insert(lhs);      }    }    for(inti=j;i>=0;i--)    {      // Iterate over the range from i to j      for(intk = i;k<=j;k++)      {        // Iterate over the rules        for(autox:R)        {          string lhs = x.first;          vector<vector<string>> rule = x.second;          for(autorhs:rule)          {          // If a terminal is found            if(rhs.size()==2 && T[i][k].find(rhs[0])!=T[i][k].end() && T[k+1][j].find(rhs[1])!=T[k+1][j].end())              T[i][j].insert(lhs);          }        }      }    }  }  // If word can be formed by rules  // of given grammar  if(T[0][n-1].size()!=0)    cout << "True\n";  else    cout << "False\n";}// Driver Codeintmain(){  // terminal symbols  terminals = {"book", "orange", "man",              "tall", "heavy",              "very", "muscular"};    // non terminal symbols  non_terminals = {"NP", "Nom", "Det", "AP",                   "Adv", "A"};    // Rules  R["NP"]={{"Det", "Nom"}};  R["Nom"]= {{"AP", "Nom"}, {"book"},              {"orange"}, {"man"}};  R["AP"] = {{"Adv", "A"}, {"heavy"},             {"orange"}, {"tall"}};  R["Det"] = {{"a"}};  R["Adv"] = {{"very"}, {"extremely"}};  R["A"] = {{"heavy"}, {"orange"}, {"tall"},            {"muscular"}};    // Given String  vector<string> w = {"a", "very", "heavy", "orange", "book"};  // Function Call  cykParse(w);  return0;} | 
Java
| importjava.util.*;classGFG{    // Non-terminals symbols  staticList<String> terminals = newArrayList<>();  staticList<String> non_terminals = newArrayList<>();  // Rules of the grammar  staticMap<String, List<List<String> > > R    = newHashMap<>();  // function to perform the CYK Algorithm  staticvoidcykParse(List<String> w)  {    intn = w.size();    // Initialize the table    Map<Integer, Map<Integer, Set<String> > > T      = newHashMap<>();    // Filling in the table    for(intj = 0; j < n; j++) {      // Iterate over the rules      for(Map.Entry<String, List<List<String> > > x :           R.entrySet()) {        String lhs = x.getKey();        List<List<String> > rule = x.getValue();        for(List<String> rhs : rule) {          // If a terminal is found          if(rhs.size() == 1              && rhs.get(0).equals(w.get(j))) {            if(T.get(j) == null)              T.put(j, newHashMap<>());            T.get(j)              .computeIfAbsent(              j, k -> newHashSet<>())              .add(lhs);          }        }      }      for(inti = j; i >= 0; i--) {        // Iterate over the range from i to j        for(intk = i; k <= j; k++) {          // Iterate over the rules          for(Map.Entry<String,               List<List<String> > > x :               R.entrySet()) {            String lhs = x.getKey();            List<List<String> > rule              = x.getValue();            for(List<String> rhs : rule) {              // If a terminal is found              if(rhs.size() == 2                  && T.get(i) != null                  && T.get(i).get(k) != null                  && T.get(i).get(k).contains(                    rhs.get(0))                  && T.get(k + 1) != null                  && T.get(k + 1).get(j)                  != null                  && T.get(k + 1)                  .get(j)                  .contains(                    rhs.get(1))) {                if(T.get(i) == null)                  T.put(i,                        newHashMap<>());                if(T.get(i).get(j) == null)                  T.get(i).put(                  j, newHashSet<>());                T.get(i).get(j).add(lhs);              }            }          }        }      }    }    // If word can be formed by rules    // of given grammar    if(T.get(0) != null&& T.get(0).get(n - 1) != null        && T.get(0).get(n - 1).size() != 0)      System.out.println("True");    else      System.out.println("False");  }  // Driver Code  publicstaticvoidmain(String[] args)  {    // terminal symbols    terminals      = Arrays.asList("book", "orange", "man", "tall",                      "heavy", "very", "muscular");    // non terminal symbols    non_terminals = Arrays.asList("NP", "Nom", "Det",                                  "AP", "Adv", "A");    // Rules    R.put("NP",          Arrays.asList(Arrays.asList("Det", "Nom")));    R.put("Nom",          Arrays.asList(Arrays.asList("AP", "Nom"),                        Arrays.asList("book"),                        Arrays.asList("orange"),                        Arrays.asList("man")));    R.put("AP", Arrays.asList(Arrays.asList("Adv", "A"),                              Arrays.asList("heavy"),                              Arrays.asList("orange"),                              Arrays.asList("tall")));    R.put("Det", Arrays.asList(Arrays.asList("a")));    R.put("Adv",          Arrays.asList(Arrays.asList("very"),                        Arrays.asList("extremely")));    R.put("A",          Arrays.asList(Arrays.asList("heavy"),                        Arrays.asList("orange"),                        Arrays.asList("tall"),                        Arrays.asList("muscular")));    // Given String    List<String> w = Arrays.asList("a", "very", "heavy",                                   "orange", "book");    // Function Call    cykParse(w);  }}// This code is contributed by lokeshpotta20 | 
Python3
| # Python implementation for the# CYK Algorithm# Non-terminal symbolsnon_terminals =["NP", "Nom", "Det", "AP",                   "Adv", "A"]terminals =["book", "orange", "man",              "tall", "heavy",              "very", "muscular"]# Rules of the grammarR ={     "NP": [["Det", "Nom"]],     "Nom": [["AP", "Nom"], ["book"],              ["orange"], ["man"]],     "AP": [["Adv", "A"], ["heavy"],             ["orange"], ["tall"]],     "Det": [["a"]],     "Adv": [["very"], ["extremely"]],     "A": [["heavy"], ["orange"], ["tall"],            ["muscular"]]    }# Function to perform the CYK AlgorithmdefcykParse(w):    n =len(w)        # Initialize the table    T =[[set([]) forj inrange(n)] fori inrange(n)]    # Filling in the table    forj inrange(0, n):        # Iterate over the rules        forlhs, rule inR.items():            forrhs inrule:                                # If a terminal is found                iflen(rhs) ==1and\                rhs[0] ==w[j]:                    T[j][j].add(lhs)        fori inrange(j, -1, -1):                            # Iterate over the range i to j + 1               fork inrange(i, j +1):                     # Iterate over the rules                forlhs, rule inR.items():                    forrhs inrule:                                                # If a terminal is found                        iflen(rhs) ==2and\                        rhs[0] inT[i][k] and\                        rhs[1] inT[k +1][j]:                            T[i][j].add(lhs)    # If word can be formed by rules     # of given grammar    iflen(T[0][n-1]) !=0:        print("True")    else:        print("False")    # Driver Code# Given stringw ="a very heavy orange book".split()# Function CallcykParse(w) | 
C#
| // C# program to implement above approachusingSystem;usingSystem.Collections;usingSystem.Collections.Generic;classGFG{    // terminal and Non-terminals symbols    // static List<string> terminals = new List<string>();    // static List<string> non_terminals = new List<string>();    // Rules of the grammar    staticDictionary<string,List<List<string>>> R = newDictionary<string,List<List<string>>>();    // function to perform the CYK Algorithm    staticvoidcykParse(List<string> w)    {        intn = w.Count;        // Initialize the table        SortedDictionary<int, SortedDictionary<int, SortedSet<string>>> T = newSortedDictionary<int, SortedDictionary<int, SortedSet<string>>>();        // Filling in the table        for(intj = 0 ; j < n ; j++)        {            // Iterate over the rules            foreach(KeyValuePair<string,List<List<string>>> x inR)            {                stringlhs = x.Key;                List<List<string>> rule = x.Value;                                foreach(List<string> rhs inrule)                {                    // If a terminal is found                    if(rhs.Count == 1 && rhs[0] == w[j]){                        if(!T.ContainsKey(j)){                            T.Add(j, newSortedDictionary<int, SortedSet<string>>());                        }                        if(!T[j].ContainsKey(j)){                            T[j].Add(j, newSortedSet<string>());                        }                        T[j][j].Add(lhs);                    }                }            }            for(inti = j ; i >= 0 ; i--)            {                // Iterate over the range from i to j                for(intk = i ; k <= j ; k++)                {                    // Iterate over the rules                    foreach(KeyValuePair<string,List<List<string>>> x inR)                    {                        stringlhs = x.Key;                        List<List<string>> rule = x.Value;                        foreach(List<string> rhs inrule)                        {                        // If a terminal is found                            if(rhs.Count == 2 &&                                T.ContainsKey(i) &&                                T[i].ContainsKey(k) &&                                T[i][k].Contains(rhs[0]) &&                                T.ContainsKey(k + 1) &&                                T[k + 1].ContainsKey(j) &&                                T[k + 1][j].Contains(rhs[1]))                            {                                if(!T.ContainsKey(i)){                                    T.Add(i, newSortedDictionary<int, SortedSet<string>>());                                }                                if(!T[i].ContainsKey(j)){                                    T[i].Add(j, newSortedSet<string>());                                }                                T[i][j].Add(lhs);                            }                        }                    }                }            }        }        // If word can be formed by rules        // of given grammar        if(T.ContainsKey(0) && T[0].ContainsKey(n - 1) && T[0][n - 1].Count != 0){            Console.Write("True\n");        }else{            Console.Write("False\n");        }    }        // Driver code    publicstaticvoidMain(string[] args){        // terminal symbols        // terminals = new List<string>{        //     "book",        //     "orange", "man",         //    "tall", "heavy",         //    "very", "muscular"        // };        // non terminal symbols        // non_terminals = new List<string>{        //     "NP", "Nom", "Det",        //     "AP", "Adv", "A"        // };        // Rules        R.Add("NP", newList<List<string>>{            newList<string>{"Det", "Nom"}        });        R["Nom"]= newList<List<string>>{            newList<string>{"AP", "Nom"},            newList<string>{"book"},             newList<string>{"orange"},            newList<string>{"man"}        };        R["AP"] = newList<List<string>>{            newList<string>{"Adv", "A"},            newList<string>{"heavy"},             newList<string>{"orange"},            newList<string>{"tall"}        };        R["Det"] = newList<List<string>>{            newList<string>{"a"}        };        R["Adv"] = newList<List<string>>{            newList<string>{"very"},            newList<string>{"extremely"}        };        R["A"] = newList<List<string>>{            newList<string>{"heavy"},            newList<string>{"orange"},            newList<string>{"tall"},             newList<string>{"muscular"}        };        // Given String        List<string> w = newList<string>{"a", "very", "heavy", "orange", "book"};        // Function Call        cykParse(w);            }}// This code is contributed by subhamgoyal2014. | 
Javascript
| // CYK Algorithm// Non-terminal symbolsconst terminals = ["book", "orange", "man",                   "tall", "heavy",                   "very", "muscular"];const non_terminals = ["NP", "Nom", "Det", "AP",                       "Adv", "A"];// Rules of the grammarconst R = {  "NP": [["Det", "Nom"]],  "Nom": [["AP", "Nom"], ["book"],           ["orange"], ["man"]],  "AP": [["Adv", "A"], ["heavy"],          ["orange"], ["tall"]],  "Det": [["a"]],  "Adv": [["very"], ["extremely"]],  "A": [["heavy"], ["orange"], ["tall"],         ["muscular"]]};// function to perform the CYK AlgorithmfunctioncykParse(w) {  let n = w.length;  // Initialize the table  let T = [];  for(let i = 0; i < n; i++) {    T[i] = [];    for(let j = 0; j < n; j++) {      T[i][j] = newSet();    }  }    // Filling in the table  for(let j = 0; j < n; j++) {    // Iterate over the rules    for(let lhs inR) {      let rule = R[lhs];      for(let rhs of rule) {        // If a terminal is found        if(rhs.length == 1 && rhs[0] == w[j]) {          T[j][j].add(lhs);        }      }    }    for(let i = j; i >= 0; i--) {      // Iterate over the range from i to j      for(let k = i; k <= j; k++) {        // Iterate over the rules        for(let lhs inR) {          let rule = R[lhs];          for(let rhs of rule) {            // If a terminal is found            if(rhs.length == 2 && T[i][k].has(rhs[0]) && T[k + 1][j].has(rhs[1])) {              T[i][j].add(lhs);            }          }        }      }    }  }  // If word can be formed by rules  // of given grammar  if(T[0][n - 1].size !== 0) {    console.log("True");  } else{    console.log("False");  }}// Given Stringconst w = ["a", "very", "heavy", "orange", "book"];// Function CallcykParse(w); | 
True
Time Complexity: O(N3) 
Auxiliary Space:O(N2)
 
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!

 
                                    ![Rendered by QuickLaTeX.com A \in T[i, j] \text{ if and only if } B \in T[i, k], C \in T[k, j] \text{ and } A \rightarrow BC \text{ is a rule of G}](https://cdn.geeksforgeeks.org/static/gallery/images/logo.png)








