A context-free grammar with start symbol , terminal alphabet , and the following productions generates all binary palindromes:Each production preserves the palindrome property: it either supplies a central string of length zero or one, or wraps the same symbol around a shorter palindrome. This proves soundness by structural induction on the parse tree.
Conversely, any palindrome of length at least two has equal first and last symbols. Remove them to obtain a shorter palindrome, apply mathematical induction on length, and then use the corresponding wrapping production. The three base cases are , , and , proving completeness.
A context-free grammar is an ambiguous grammar if some terminal string has two distinct parse trees, equivalently two distinct leftmost derivations. Different orders of expanding independent nonterminals do not by themselves constitute ambiguity.
The context-free grammar just given is unambiguous. It suffices to prove this for the reversed productions, because reversing a parse tree preserves whether two parse trees are distinct.
For , the empty string forces the empty production. A nonempty string must use . In any resulting nonnegative balanced path, the displayed one is the unique first return to zero. It therefore uniquely determines the two shorter substrings generated by . Mathematical induction on length makes both smaller parse trees unique.
For , the empty string again forces the empty production. A nonempty path with no return to zero must use , since always returns to zero. A path with a return must use , since stays positive after its first step. In the latter case, the first return uniquely determines the substring and the remaining substring. Applying mathematical induction to these shorter substrings proves uniqueness throughout. Hence
Use the following context-free grammar, with start symbol :The symbol generates the balanced members of the suffix-nonnegative binary language, while permits extra zeros. To prove the claim and prepare the ambiguity argument, reverse every generated string. The reversed productions areHere represents the reversals of the strings, and represents the reversals of the strings. A string belongs to the desired formal language precisely when its reversal has no string prefix with more ones than zeros. Associate a path with each reversed string, counting a zero as a step and a one as a step .
The nonterminal generates exactly the paths that stay nonnegative and finish at zero. The production consists of an initial up-step, a balanced path one level higher, a down-step to zero, and another balanced path. Conversely, any nonempty balanced nonnegative path has a first return to zero, and splitting there gives exactly this form. Mathematical induction on length proves the characterization.
The productions for generate nonnegative paths: stays strictly above zero after its first step, while first executes a balanced excursion and then another nonnegative path. Conversely, a nonempty nonnegative path starts with zero. If it never returns to height zero, removing that initial zero leaves a nonnegative path, giving . If it does return, split at its first return to obtain . These shorter components can be generated by mathematical induction. This proves that the original context-free grammar generates exactly the required formal language.
For instance, in the original context-free grammar,while . The string is excluded since its entire length is a string suffix having two ones and only one zero.
The assertion is true. Every single finite string over is denoted by the regular expression . The empty string is denoted by . Hence if is a finite formal language, the regular expressiondenotes precisely , with each read as its concatenation of symbols. This uses the defining union and concatenation operations for regular expressions. For , use the regular expression . Therefore
The assertion is false. Use the pumping lemma for regular languages: if is a regular language, there is such that every with admits a factorization with , , and for every integer .
Suppose the stated formal language were a regular language, and take . This string has twice as many zeros as ones, so it belongs to . In every allowed factorization, the first positions are all zeros, forcing with . Pumping down with producesIt still has ones but fewer than zeros, contradicting the pumping lemma for regular languages. Thus
The assertion is false. Suppose the formal language were a regular language, and let be its constant from the pumping lemma for regular languages. ConsiderIn every decomposition with and , the pumped factor is with . Pumping down givesThis string has exactly two ones and ends in a one. If it were , each copy of would contain exactly one one, and each copy would end in a one. The first copy would consequently be , while the second would be . They have different lengths because , so they cannot be equal. This contradiction to the pumping lemma for regular languages proves
The assertion is true. Write and . The semantics of regular expressions reduce the claim towhere juxtaposition is concatenation of formal languages and a star is the Kleene star.
Every element of is a finite concatenation of members of , so belongs to . Concatenating any finite number of such elements still belongs to , proving .
A Turing machine has a finite control, an unbounded tape divided into cells over a finite tape alphabet including a blank symbol, and a head that reads one cell at a time. For a deterministic Turing machine, the current state and scanned symbol determine the replacement symbol, a head move one cell left or right, and the next state. The input occupies finitely many cells initially; all other cells are blank. Distinguished halting states report acceptance or rejection.
A formal language is a decidable language if some Turing machine halts on every input string, accepting exactly the members and rejecting all nonmembers:Halting on the nonmembers is part of the requirement; a machine that merely runs forever on some nonmembers need not decide the formal language.
The halting problem asks, given the encoding of a Turing machine and an input string, whether that machine eventually halts on that input. Its formal language isSuppose a Turing machine decided this formal language. Construct another Turing machine that, on an input encoding , runs on . If answers that halts on its own encoding, enters an infinite loop. If answers that does not halt, halts. Handling malformed encodings by immediate rejection makes well-defined on arbitrary strings.
Now run on its own encoding. If says that halts, the construction makes loop; if says that does not halt, the construction makes halt. Either answer contradicts the assumed correctness of . ThereforeThe contradiction concerns a decider that must give a correct terminating answer for every encoded machine and input. Simulating does detect a halt when one occurs, but cannot in general certify that a running computation will continue forever.
A regular language over a finite alphabet is a formal language recognized by a deterministic finite automaton; equivalently it is described by a regular expression. A nondeterministic finite automaton accepts an input string if at least one permitted computation consumes that string and reaches an accepting state. The connection is the equivalenceEvery deterministic finite automaton is a special case of a nondeterministic finite automaton. Conversely, the subset construction makes a deterministic finite automaton whose states record the set of all possible current states of a nondeterministic finite automaton. After each input symbol, update that set by taking all permitted transitions, also including any epsilon closure. A new state is accepting exactly when it contains an old accepting state. A finite state set has finitely many subsets, so this really is a deterministic finite automaton recognizing the same formal language.
This is a regular language. A deterministic finite automaton needs only to remember the number of consecutive trailing ones, up to three, and whether a forbidden run of four has already occurred. Use states , start at , and accept in . Its transition function of a finite automaton is:
| State | On | On |
|---|---|---|
These strings are the even-length palindromes . Suppose this were a regular language, and let be its length from the pumping lemma for regular languages. The stringbelongs to the formal language, with . In any allowed decomposition , where and , the pumped block is with . Pumping it away givesThis is not a palindrome: its initial and final runs of zeros have different lengths. Hence it cannot have the form , contradicting the pumping lemma for regular languages. Therefore
Every finite formal language is a regular language. A single string is described by the regular expression formed by concatenating its symbols; the empty string is described by . A finite union of these regular expressions describes any specified finite collection of strings. The empty collection is described by . ThusEquivalently, a deterministic finite automaton can remember which prefix of one of the finitely many accepted strings has been read, moving to a rejecting dead state when no such prefix remains possible.
A context-free grammar is a quadruple : is a finite set of nonterminal symbols, is a disjoint finite alphabet of terminal symbols, is the start symbol, and is a finite set of productions , with and . Its formal language consists of the terminal strings derivable from .
For the even-length palindromes in (b)(ii), take terminal alphabet and the context-free grammarEvery production adds identical symbols at the two ends, so every generated string is an even-length palindrome. Conversely, remove the matching outer pair of any nonempty even-length palindrome and repeat; this constructs its derivation, ending with .
For the unequal-block formal language in (b)(iv), useAfter uses of , and zeros from , the resulting string is , so its zero count is larger. Every such unequal pair with occurs by choosing . Similarly generates exactly with , covering precisely . The start alternatives cover both possibilities and never equal counts. If positive exponents are required, replace the start productions by , which adds one matched pair and ensures both counts are positive.
A word can be a string, and the input text a list of those strings. Use names that avoid clashing with the Standard Prelude machine-word type
Word:type TextWord = String
type Text = [TextWord]Thus . These are type synonyms, so ordinary string and list operations remain available without wrapping or unwrapping constructors. They describe input that has already been tokenized; punctuation and word boundaries do not need a separate treatment here.
Normalize each word once by applying
toLower to its characters. Maintain an association list whose keys are distinct normalized words and whose values are their frequencies:import Data.Char (toLower)
type TextWord = String
type Text = [TextWord]
type Frequency = Integer
type Entry = (TextWord, Frequency)
count :: Text -> [Entry]
count = foldl addWord [] . map (map toLower)
where
-- Increment the unique matching key, or append a new key with count one.
addWord :: [Entry] -> TextWord -> [Entry]
addWord [] w = [(w, 1)]
addWord ((v, n):rest) w
| w == v = (v, n + 1) : rest
| otherwise = (v, n) : addWord rest wAfter any input prefix, each normalized word seen so far occurs exactly once as a key, with its number of occurrences as value. This loop invariant holds initially for the empty association list. A step either increments the unique existing entry or introduces exactly one new entry, so mathematical induction provesfor every word that occurs. In particular,
Baa and baa contribute to the same key baa; the three frequency-two keys in the source example are baa, yes and sir.The association list preserves first-occurrence order, although the specification allows any order here. For input words and distinct normalized words, it performs key comparisons in the worst case. Character normalization costs the total input character count, and a string comparison can inspect multiple characters. Characterwise
toLower supplies the intended case-insensitive comparison convention for the example; full Unicode case folding can require a different normalization policy.Traverse the trie along the input string, stopping as soon as the required child is absent. All words and letter collections in this question are assumed to use only lowercase letters The loop invariant is that
a through z; this is the alphabet represented by the given child array. Put the following methods inside the supplied Trie class.def contains(w: String): Boolean = {
var t: Trie = this
var i = 0
while (i < w.length && t != null) {
t = t.children(toIndex(w.charAt(i)))
i += 1
}
t != null && t.complete
}t is the trie node reached by the prefix w.take(i), or null if that prefix has no path, with . If t is null, no extension can be a stored word. Otherwise, following the next character preserves the loop invariant. The loop variant is . At exit,A prefix that exists but is not complete must return false. The empty string is present exactly when the root's complete flag is true. The time complexity is , with constant auxiliary space.Create only the missing nodes along the required trie path, and mark its last node complete.The loop invariant is that the path for
def add(w: String): Unit = {
var t: Trie = this
var i = 0
while (i < w.length) {
val k = toIndex(w.charAt(i))
if (t.children(k) == null) t.children(k) = new Trie
t = t.children(k)
i += 1
}
t.complete = true
}w.take(i) exists and ends at t, while the set of complete words remains unchanged from entry. A fresh node has all children null and complete == false, so creating an unfinished path introduces no stored word. The loop variant is . At termination the full path exists; setting its final flag givesExisting paths and terminal flags are untouched, so adding an existing word is idempotent. Adding the empty string marks the root. The time complexity is because the alphabet size is fixed; at most new trie nodes are allocated.Use trie search with a letter multiset. Counts avoid generating the same result repeatedly when the input contains equal letters. The backtracking explores a letter value once, irrespective of how many indistinguishable copies remain.At entry to
def allWords(ls: String): List[String] = {
val left = new Array[Int](26)
var i = 0
while (i < ls.length) {
left(toIndex(ls.charAt(i))) += 1
i += 1
}
var out = List.empty[String]
def visit(t: Trie, prefix: String): Unit = {
if (t.complete) out = prefix :: out
var k = 0
while (k < 26) {
val child = t.children(k)
if (left(k) > 0 && child != null) {
left(k) -= 1
visit(child, prefix + toChar(k))
left(k) += 1
}
k += 1
}
}
visit(this, "")
out.reverse
}visit, t is reached by prefix, and left(k) is the input multiplicity of letter minus its multiplicity in prefix. All these counts are nonnegative. Each recursive call consumes one available letter, and restoration makes sibling calls see the same counts as before that child. The recursion therefore terminates at depth at most , even when the dictionary contains longer words.A completed prefix is emitted once, and the traversal continues so that longer words with the same prefix are also found. Every emitted word is in the dictionary and uses at most the available multiplicities. Conversely, every dictionary word meeting those bounds has an existing trie path whose next letter has positive remaining count at every step; the traversal reaches its terminal node. A trie has a unique root path for each word, and each child is visited at most once per reached node. ThereforeThe boxed expression specifies the set of entries in the returned list, rather than its ordering. If the dictionary includes the empty string, the method includes it once. Counting costs ; visiting reachable nodes costs apart from string copying and output construction. The presented immutable-prefix construction additionally costs the sum of the prefix lengths it constructs. The recursion depth is at most , and the letter-count array has fixed size.
If the mutable set were allowed a covariant type parameter, both variables below could refer to the same mutable object:The broad view accepts any value, including a string. The narrow view still promises that deletion returns an
val s1: Set[Int] = new Set[Int](List())
val s2: Set[Any] = s1 // hypothetically permitted
s2.add("not an integer")
val n: Int = s1.delete() // cannot return an IntInt, yet the sole stored value is that string. This violates type safety:This is covariance failure for a mutable collection. The input occurrence in add(x: T) conflicts with the positive occurrence in the result of delete. Keeping a generic type invariance restriction prevents the unsafe aliasing. The example uses an initially empty set so that the problematic deletion result is forced.