the core of Flex is a finite state automaton (check google for some theory about it). every new character is checked against a character table and will lead to a new state where new characters are available.
For instance, imagine you want to translate ":-)" into "joy.gif", ":-(" into "sad.gif", ">:[" in "angry.gif" and "=]" in "l337.gif"
Code:
state0 : {':' --> state1 , '>' --> state2, '=' --> state3}
state1 : {'-' --> state4}
state2 : {':' --> state5}
state3 : {']' --> accept("l337.gif")}
state4 : {')' --> accept("joy.gif"), '(' --> accept("sad.gif")}
state5 : {'-' --> state6}
state6 : {'[' --> accept("angry.gif")}
that's it ... you start in state0 and try to follow the existing chars. If you can't, just output the first char of the current string and go on with the second one (so that ::-) is translated into :joy.gif)