Your language:
L = {v1w | v, w in {a, b, c}* and #a(v) = #c(w)}
The first thing to notice is that b
s and c
s in the first part of the string can be ignored, whereas a
s and b
s in the second part of the string can be ignored. In the first part of the string, push onto the stack; and in the second part of the string, pop off. You know you've moved from the first part of the string to the second when you read a 1
. Reject if you (a) run out of stack before you're done seeing c
s in the second part of the string or (b) if you still have stuff on the stack when you're done reading the second part of the string. If you run out of input with an empty stack, accept. Something like this:
Q s S | Q' S'
---------------+---------------
// first part of the string
---------------+---------------
q0 a Z/a | q0 aZ/aa
q0 b Z/a | q0 Z/a
q0 c Z/a | q0 Z/a
---------------+---------------
// transition to 2nd part
---------------+---------------
q0 1 Z/a | q1 Z/a
---------------+---------------
// second part of the string
---------------+---------------
q1 a Z/a | q1 Z/a
q1 b Z/a | q1 Z/a
q1 c a | q1 -
We can define acceptance to be by empty stack in state q1
. If you want a separate accepting state you can add a new one and an empty transition for that purpose, although that results in a non-deterministic PDA. We can get a DPDA with a separate accepting state if we really want to as follows:
- read
b
s and c
s in a loop on the initial state (accepting).
- if you read an
a
, go to state A
. In state A
, read b
s and c
s in a loop, and if you read a
s, push a
s onto the stack.
- from the initial, upon seeing
1
, go to state E
, which is accepting, and loop on b
s and c
s. Crash on an a
, or transition to a dead state.
- from
A
, upon seeing 1
, go to state C
read a
s and b
s in a loop, and if you read c
with an a
on the stack, pop the a
. If you read a c
with Z
on top of the stack, transition to B
.
- state
B
loops to itself on a
and b
and crashes, or transitions to a dead state, on c
.