Example #1
0
        static void Main(string[] args)
        {
            Console.WriteLine("Введите регулярное выражение (шаблон)(^ - звездочка Клини, + - плюс, * - умножить):");
            var n   = Console.ReadLine();
            var exp = RPN.GetExpression(n);

            Console.WriteLine("Регулярное выражение в обратной польской нотации: " + exp);
            var nfa = EpsilonNFA.BuildEpsilonNFAByExp(exp);

            Console.WriteLine("Введите текст:");
            var text  = Console.ReadLine();
            var index = EpsilonNFA.ByPassENFA(nfa, text);

            if (index == -1)
            {
                Console.WriteLine("Индекса нет");
            }
            else
            {
                Console.WriteLine("Минимальный индекс j = " + index);
            }
        }
        //метод создания эпсилон-НКА по регулярному выражению, записанному в обратной польской нотации
        public static EpsilonNFA BuildEpsilonNFAByExp(string exp)
        {
            //в стеке будут лежать автоматы
            //в конце алгоритма в стеке останется один, искомый, автомат
            var stack = new Stack <EpsilonNFA>();
            var i     = 0;

            //идем по символам в выражении
            foreach (var symbol in exp)
            {
                //если символ не звездочка Клини, не + и не *, то строим автомат вида О->symbol->O и кладем в стек
                if (symbol != '^' && symbol != '+' && symbol != '*')
                {
                    var nfa = new EpsilonNFA(new Node(i), new Node(i + 1), symbol.ToString());
                    i += 2;
                    stack.Push(nfa);
                }
                //если символ *, то берем из стека два последних автомата и соединяем их по правилу
                //затем кладем в стек
                else if (symbol == '*')
                {
                    var nfa2 = stack.Pop();
                    var nfa1 = stack.Pop();
                    i += 1;
                    Node.Connect(nfa1.Finish, nfa2.Start, "eps");
                    nfa1.Nodes.AddRange(nfa2.Nodes);
                    nfa1.Finish = nfa2.Finish;
                    stack.Push(nfa1);
                }
                //аналогично * (также по правилу)
                else if (symbol == '+')
                {
                    var nfa2   = stack.Pop();
                    var nfa1   = stack.Pop();
                    var Start  = new Node(Math.Min(nfa1.Start.NodeNumber, nfa2.Start.NodeNumber) - 1);
                    var Finish = new Node(Math.Max(nfa1.Finish.NodeNumber, nfa2.Finish.NodeNumber) + 1);
                    i++;

                    Node.Connect(Start, nfa1.Start, "eps");
                    nfa1.Start = Start;
                    nfa1.Nodes.Add(Start);

                    Node.Connect(Start, nfa2.Start, "eps");
                    nfa2.Start = Start;
                    nfa1.Nodes.AddRange(nfa2.Nodes);

                    Node.Connect(nfa1.Finish, Finish, "eps");
                    nfa1.Nodes.Add(Finish);
                    nfa1.Finish = Finish;

                    Node.Connect(nfa2.Finish, Finish, "eps");
                    nfa2.Finish = Finish;
                    stack.Push(nfa1);
                }
                //аналогично
                else if (symbol == '^')
                {
                    var nfa    = stack.Pop();
                    var Start  = new Node(nfa.Start.NodeNumber - 1);
                    var Finish = new Node(nfa.Finish.NodeNumber + 1);
                    i++;
                    Node.Connect(Start, Finish, "eps");
                    Node.Connect(nfa.Finish, nfa.Start, "eps");
                    Node.Connect(Start, nfa.Start, "eps");
                    Node.Connect(nfa.Finish, Finish, "eps");
                    nfa.Start  = Start;
                    nfa.Finish = Finish;
                    nfa.Nodes.Add(Start);
                    nfa.Nodes.Add(Finish);
                    stack.Push(nfa);
                }
            }

            var sortedNodes = stack.Peek().Nodes.OrderBy(v => v.NodeNumber).ToList();

            for (var j = 0; j < stack.Peek().Nodes.Count - 1; j++)
            {
                if (sortedNodes[j].NodeNumber == sortedNodes[j + 1].NodeNumber)
                {
                    for (var l = j + 1; l < sortedNodes.Count; l++)
                    {
                        sortedNodes[l].NodeNumber++;
                    }
                }
            }
            //возвращаем автомат
            return(stack.Pop());
        }
        //метод обхода автомата
        public static int ByPassENFA(EpsilonNFA nfa, string text)
        {
            //метод находит такой наименьший индекс k, что a1..ak является словом из языка, определяемым регулярным выражением
            //если слово полностью прошло по автомату и не прочиталось, то уменьшаем слово с начала
            //в результате получаем, что как только слово прочитается, то мы получим минимальный индекс k,
            //для которого существует некоторый j<k : аj..ak - слово из языка, определяемым регулярным выражением
            //reducerCount нужен для восстановления индекса в начальном слове
            var reducerCount = 0;

            //сам метод выполнен по алгоритму
            while (text.Length != 0)
            {
                var states = new List <Node> [text.Length + 1];
                states = states.Select(v => v = new List <Node>()).ToArray();
                for (var i = -1; i < text.Length; i++)
                {
                    var visied = new HashSet <Node>();
                    var list   = new List <Node>();
                    if (i == -1)
                    {
                        states[text.Length].Add(nfa.Start);
                        list.AddRange(states[text.Length]);
                    }
                    else
                    {
                        //находим вершины, которые были в предыдущем наборе состояний и у которых есть ребро, которое направлено в них, и вес этого ребра == символу и слове
                        if (i == 0)
                        {
                            states[i].AddRange(nfa.Nodes.Where(v => states[text.Length].Contains(v)).SelectMany(v => v.IncidentNodes.Where(l => l.IncidentEdges.Where(k => k.To == l && k.Weight == text[i].ToString()).Count() != 0)));
                        }
                        else
                        {
                            states[i].AddRange(nfa.Nodes.Where(v => states[i - 1].Contains(v)).SelectMany(v => v.IncidentNodes.Where(l => l.IncidentEdges.Where(k => k.To == l && k.Weight == text[i].ToString()).Count() != 0)));
                        }
                        list.AddRange(states[i]);
                    }
                    while (list.Count != 0)
                    {
                        {
                            var curNode = list.FirstOrDefault();
                            list.Remove(curNode);
                            //ищем вершины, в которых есть ребро, направленное от вершины, вес которого равен эпсилон
                            var neededNodes = curNode.IncidentNodes.Where(v => v.IncidentEdges.Where(l => l.From == curNode).Where(l => l.Weight == "eps").Count() != 0);
                            foreach (var node in neededNodes)
                            {
                                if (!visied.Contains(node))
                                {
                                    visied.Add(node);
                                    if (i == -1)
                                    {
                                        states[text.Length].Add(node);
                                    }
                                    else
                                    {
                                        states[i].Add(node);
                                    }
                                    list.Add(node);
                                }
                            }
                        }
                    }
                    //после каждой итерации проверяем, не появилось ли в текущем наборе состояний финального
                    //если появлиось, то мы нашли минимальный k : a1..ak - слово из языка, определяемого регулярным выражением
                    if (i == -1)
                    {
                        if (states[text.Length].Contains(nfa.Finish))
                        {
                            return(reducerCount + i);
                        }
                    }
                    else
                    {
                        if (states[i].Contains(nfa.Finish))
                        {
                            return(reducerCount + i);
                        }
                    }
                }
                //если не случилось возврата - обрезаем строку
                text = text.Substring(1);
                reducerCount++;
            }
            //если не случилось ни одного возврата, то в строке нет подстроки : подстрока является словом, определяемым регулярным выражением
            return(-1);
        }