/**
         * Build lattice from input text
         *
         * @param text  source text for the lattice
         * @return built lattice, not null
         */
        public ViterbiLattice Build(string text)
        {
            int            textLength = text.Length;
            ViterbiLattice lattice    = new ViterbiLattice(textLength + 2);

            lattice.AddBos();

            int unknownWordEndIndex = -1; // index of the last character of unknown word

            for (int startIndex = 0; startIndex < textLength; startIndex++)
            {
                // If no token ends where current token starts, skip this index
                if (lattice.TokenEndsWhereCurrentTokenStarts(startIndex))
                {
                    string suffix = text.Substring(startIndex);
                    bool   found  = ProcessIndex(lattice, startIndex, suffix);

                    // In the case of normal mode, it doesn't process unknown word greedily.
                    if (searchMode || unknownWordEndIndex <= startIndex)
                    {
                        int[] categories = characterDefinitions.LookupCategories(suffix[0]);

                        for (int i = 0; i < categories.Length; i++)
                        {
                            int category = categories[i];
                            unknownWordEndIndex = ProcessUnknownWord(category, i, lattice, unknownWordEndIndex, startIndex, suffix, found);
                        }
                    }
                }
            }

            if (useUserDictionary)
            {
                ProcessUserDictionary(text, lattice);
            }

            lattice.AddEos();

            return(lattice);
        }