1102. Strange Dialog
Time limit: 1.0 second
Memory limit: 16 MB
One entity named "one" tells with his friend "puton" and their conversation is interesting. "One" can say words "out" and "output", besides he calls his friend by name. "Puton" can say words "in", "input" and "one". They understand each other perfect and even write dialogue in strings without spaces.
You have
N strings. Find which of them are dialogues.
Input
In the first line of input there is one non-negative integer
N ≤ 1000. Next
N lines contain non-empty strings. Each string consists of small Latin letters. Total length of all strings is no more than 10
7 characters.
Output
Output consists of
N lines. Line contains word "YES", if string is some dialogue of "one" and "puton", otherwise "NO".
Sample
|
/**背包DP**/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 10000007
bool dp[maxn];
char s[6][7]= {"out","output","puton","in","input","one"};
int len[6]= {3,6,5,2,5,3};
int n;
char str[maxn+1];
void read()
{
void DP();
scanf("%d",&n);
getchar();
for(int i=0; i<n; i++)
{
gets(str);
DP();
}
}
/**dp[i]表示,i位置前的字符是否是所给字典中的单词**/
void DP()//动态规划核心
{
memset(dp,0,sizeof(dp));
int l=strlen(str);
dp[0]=1;
for(int i=0; i<l; i++)//以输入的字符串的长度为阶段
{
if(dp[i])//如果当前字符前的单词是所给的字典中的单词,判断i及i后面有限长度的字符所构成的字符串是否是字典中的单词
for(int j=0; j<6; j++)//枚举字典中的单词
if(i+len[j]<=l)//选择出要构造的单词,如果构造的长度大于str剩下的,就不能构造
{
int flag=1;
for(int k=0; k<strlen(s[j]); k++)//对后面所构成的单词进行匹配,如果没有成功返回0
if(str[i+k]!=s[j][k])
flag=0;
if(flag)
dp[i+len[j]]=1;//如果匹配成功,说明i+len[j]这一段是字典中的单词
}
}
if(dp[l])//递推到位置l,判断l之前的所有单词是否都存在于字典中
puts("YES");
else
puts("NO");
}
int main()
{
read();
return 0;
}
有大神这样做:
题意
one 和 puton 这两个人进行交谈。one 只能够说:out、output 和 puton 这三个单词。而 puton 只能说 in、input 和 one 这三个单词。她们之间的对话是由单词直接连接而成(单词之间没有空格)。
你的任务是判断给定的输入(该输入仅包含小写拉丁字母)是否为合法的对话。
数学背景
一个有限自动机(deterministic finite automaton, DFA) M 是一个 5-元组(Q, q0, A, Σ, δ),其中:
- Q 是状态的有限集合
- q0 ∈ Q 是初始状态
- A
Q 是一个接受状态集合
- Σ 是有限的输入字母表
- δ 是一个从 Q × Σ 到 Q 的函数,称为 M 的转移函数
有限自动机开始于状态 q0,每次读入输入字符串的一个字符。如果有限自动机在状态 q 时读入了输入字符 a,则它从状态 q 变为状态 δ(q, a)(进行了一次转移)。每当其当前状态 q 属于 A 时,就说自动机 M 接受了迄今为止所读入的字符串。没有被接受的输入称为被拒绝的输入。
很多字符串匹配算法都要建立一个有限自动机,它通过对文本字符串 T 进行扫描的方法,找出模式 P的所有出现位置。用于字符串匹配的自动机都是非常有效的:它们只对每个文本字符检查一次,并且检查每个文本字符的时间为常数。因此,在建立好自动机后所需要的时间为 Θ(n)。
解题思路
我们的任务是判断输入是否只由题目中所给出的六个单词组成。这是一个多模式字符串匹配问题,共有六个模式。
现在,首先需要根据所给的模式构造出相应的字符串匹配自动机,如下所示:
- 状态集合 Q = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 99 }
- 初始状态 q0= 0
- 接受状态集合 A = { 0, 1, 2, 3 }
- 输入字母表 Σ = { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }
- 转移函数 δ 用状态转换图表示如下:
这个字符串匹配自动机有以下几个特点:
- 她是一个多模式的字符串匹配自动机
- 她有一个特殊的状态 99,每当该自动机在状态 q 读入了输入字符 a 时,如果在上面的状态转换图中找不到相应的有向边,就转移到这个特殊状态。并且,立即停止该自动机,返回匹配失败。
- 她有多个接受状态,从初始状态 0 开始连续编号。这是为了在程序中方便判断是否匹配。
这个状态转换图中的构造由以下六个模式开始:
one | 0 –> 4 –> 5 –> 0* |
puton | 0 –> 11 –> 12 –> 13 –> 14 –> 0* |
in | 0 –> 7 -> 1* |
out | 0 –> 4 –> 6 –> 1* |
input | 0 –> 7 –> 1* –> 8 –> 9 -> 2* |
output | 0 –> 4 –> 6 –> 1* –> 8 –> 9 -> 2* |
然后,就需要仔细考虑各种状态之间的转移关系了。
最后,相应的 C# 程序如下所示:
using System; namespace Skyiv.Ben.Timus { // http://acm.timus.ru/problem.aspx?space=1&num=1102 sealed class T1102 { static readonly int[] a = { 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 3, 4, 5, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0 }; static readonly int[,] delta = { // * e i n o p t u 有限自动机的转移函数 { 99, 99, 7, 99, 4, 11, 99, 99 }, // 0 接受状态,初始状态 { 99, 99, 7, 99, 4, 8, 99, 99 }, // 1 接受状态 { 99, 99, 7, 99, 10, 11, 99, 99 }, // 2 接受状态 { 99, 0, 7, 99, 4, 11, 99, 99 }, // 3 接受状态 { 99, 99, 99, 5, 99, 99, 99, 6 }, // 4 状态 { 99, 0, 99, 99, 99, 99, 99, 99 }, // 5 状态 { 99, 99, 99, 99, 99, 99, 1, 99 }, // 6 状态 { 99, 99, 99, 1, 99, 99, 99, 99 }, // 7 状态 { 99, 99, 99, 99, 99, 99, 99, 9 }, // 8 状态 { 99, 99, 99, 99, 99, 99, 2, 99 }, // 9 状态 { 99, 99, 99, 3, 99, 99, 99, 6 }, // 10 状态 { 99, 99, 99, 99, 99, 99, 99, 12 }, // 11 状态 { 99, 99, 99, 99, 99, 99, 13, 99 }, // 12 状态 { 99, 99, 99, 99, 14, 99, 99, 99 }, // 13 状态 { 99, 99, 99, 0, 99, 99, 99, 99 }, // 14 状态 }; static void Main() { for (int c, q = 0, n = int.Parse(Console.ReadLine()); n > 0; n--, q = 0) { while ((c = Console.Read()) != '\n') if (q < 99 && c != '\r') q = delta[q, a[c - 'a']]; Console.WriteLine((q < 4) ? "YES" : "NO"); } } } }
由于模式中只包括 e、i、n、o、p、t 和 u 这七个字母,数组 a (该数组包括二十六个元素,依次对应二十六个小写拉丁字母)将输入字母表映射为从 0 到 7 的整数,1 到 7 依次对应前面的七个字母,0 对应其它字母。然后,二维数组 delta 表示转移函数 δ,直接由上面的状态转换图得到。剩下的事情就很简单了,Main 方法在 for 循环中依次读入各输入行,然后在 while 循环中执行这个自动机,之后根据自动机的状态输出是否匹配。