P1624 单词缩写

P1624 单词缩写

题目描述

树树发现好多计算机中的单词都是缩写,如GDB是全称Gnu DeBug的缩写。但是,有时候缩写对应的全称会不固定,如缩写LINUX可以理解为:

(1) LINus’s UniX

(2) LINUs’s miniX

(3) Linux Is Not UniX

现在树树给出一个单词缩写,以及一个固定的全称(若干个单词组成,空格隔开)。全

称中可能会有无效的单词,需要忽略掉,一个合法缩写要求每个有效单词中至少有一个字符出现在缩写中,所写必须按顺序出现在全称中。

对于给定的缩写和一个固定的全称,问有多少种解释方法?解释方法为所写的每个字母在全称每个有效单词中出现的位置,有一个字母位置不同,就认为是不同的解释方法。

输入输出格式

输入格式:

第一行输入一个N,表示有N个无效单词;

接下来N行分别描述一个由小写字母组成的无效单词;

最后是若干个询问,先给出缩写(只有大写字母),然后给出一个全称,读入以“LAST CASE”结束。

[数据规模]

1≤N≤100,每行字符串长度不超过150,询问次数不超过20,最后方案数不超过10^9。

输出格式:

对于每个询问先输出缩写,如果当前缩写不合法,则输出“is not a valid abbreviation”,否则输出“can be formaed in i ways”(i表示解释方法数)

输入输出样例

输入样例#1: 复制
2
and
of
ACM academy of computer makers
RADAR radio detection and ranging
LAST CASE
输出样例#1: 复制
ACM can be formed in 2 ways
RADAR is not a valid abbreviation

洛谷题解

先暴力去掉所有的单词,只对有效单词进行计算。

dp[i][j]表示前i个单词使用了前j个大写字母的方案数

初始条件dp[0][0]=1

转移:若第i个单词使用第j到第j+k个大写字母的方案数为temp[k]

则dp[i][j+k]=dp[i-1][j-1]*temp[k]

最终ans=dp[n][m]

时间复杂度O(L1*N*L2*L2)

空间复杂度O(L1*N*L2)

L1缩写长度 N全称中单词的个数 L2全称中一个单词的长度

这属于两个串匹配的问题,就一个前i,一个前j就好,反正是找子问题。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 char w[101][150];
 7 char s[150],let[150];
 8 char over[9]={'L','A','S','T',' ','C','A','S','E'};
 9 struct word
10 {
11     char c[150];
12     int l;
13 }a[150];
14 int t,n,m,len;
15 int dp[100][150],temp[150];
16 bool Case_is_Over()
17 {
18     for (int i=0;i<=8;i++)
19         if (s[i]!=over[i]) return false;
20     return true;
21 }
22 int main()
23 {
24     freopen("abbr.in","r",stdin);
25     freopen("abbr.out","w",stdout);
26     scanf("%d
",&t);
27     for (int i=1;i<=t;i++) scanf("%s",w[i]);
28     while (233)
29     {
30         char ch=getchar();len=0;
31         while ( ! ( (ch>='a'&&ch<='z') || (ch>='A'&&ch<='Z') || ch==' ' ) ) ch=getchar();
32         while ( (ch>='a'&&ch<='z') || (ch>='A'&&ch<='Z') || ch==' ' ) s[len++]=ch,ch=getchar();
33         n=m=0;//设每个询问有n个单词,m个大写字母
34         if (Case_is_Over()) break;
35         int p=0;
36         for (int i=1;i<150;i++) memset(a[i].c,0,sizeof(a[i].c)),a[i].l=0;
37         while (s[p]>='A'&&s[p]<='Z')
38         {
39             printf("%c",s[p]);
40             let[++m]=s[p]+32;
41             p++;
42         }
43         p++;
44         while (p<len)
45         {
46             n++;
47             while (s[p]>='a'&&s[p]<='z')
48             {
49                 a[n].c[a[n].l++]=s[p];
50                 p++;
51             }
52             for (int i=1;i<=t;i++)
53                 if (strcmp(w[i],a[n].c)==0)
54                 {
55                     memset(a[n].c,0,sizeof(a[n].c));
56                     a[n].l=0;
57                     n--;break;
58                 }
59             p++;
60         }
61         memset(dp,0,sizeof(dp));
62         dp[0][0]=1;
63         for (int i=1;i<=n;i++) //枚举单词
64             for (int j=i;j<=i+m-n;j++) //枚举当前单词使用大写字母的起始位置
65             {
66                 memset(temp,0,sizeof(temp));
67                 for (int p=0;p<a[i].l;p++) //第i个单词的第p个位置
68                     for (int k=min(i+m-n-j,p);k>=0;k--) //恰好等于第j+k个大写字母
69                         if (let[j+k]==a[i].c[p])
70                             if (k) temp[k]+=temp[k-1];
71                             else temp[k]++;
72                 for (int k=i+m-n-j;k>=0;k--)
73                     dp[i][j+k]+=dp[i-1][j-1]*temp[k];
74             }
75         if (dp[n][m]) printf(" can be formed in %d ways
",dp[n][m]);
76         else printf(" is not a valid abbreviation
");
77     }
78     return 0;
79 }

看大家都是N^4或N³算法,N²算法还没有,赶紧水一发

基本思路:

把所有串连接起来,记录每个串的尾后位置,

设f[k][i]表示现在在处理缩写的第K个字符,在大字符串中第I个位置对前面字符的总贡献数,则f[k][i]=Σf[k-1][j],

其中j表示满足条件的前一个缩写字符。其中满足条件的定义为“无空缺单词,且是前一个字母”。则答案为Σf[最后一个字符][j],(1<=j<=n)。

为避免出现同一字母出现位置的不同,用一个数组进行标记即可。

标记要一次打完才能更新!!!

  1 #include<bits/stdc++.h>
  2 #define RG register
  3 using namespace std;
  4 
  5 int n;
  6 char a[110][200];//无效单词
  7 char c1[200];
  8 char c[200];
  9 char all[200][200];//有效单词
 10 int ma[26][200];//每个字母的有效位置
 11 int tag[200];
 12 int tag1[200];//两个标记
 13 int cut[200];//单词的尾后位置
 14 int sum[26];//出现的次数
 15 int f[200][200];//dp数组
 16 
 17 int main()
 18 {
 19     scanf("%d
",&n);
 20     for(RG int i=1;i<=n;i++)
 21     {
 22         scanf("%s",a[i]);
 23     }
 24     scanf("%s",c1);
 25     while(1)
 26     {
 27         RG int i_1_1=1;
 28         strcpy(c,c1);
 29         if(strcmp(c,"LAST")==0)
 30         {
 31             scanf("%s",all[i_1_1++]);
 32             if(strcmp(all[i_1_1-1],"CASE")==0) break;
 33         }
 34         while(cin>>all[i_1_1++])
 35         {
 36             RG int j=0;
 37             int len=strlen(all[i_1_1-1]);
 38             while(all[i_1_1-1][j]<'a'&&j<len) j++;
 39             if(j>=len)
 40             {
 41                 strcpy(c1,all[i_1_1-1]);
 42                 i_1_1--;
 43                 break;
 44             }
 45             else
 46             {
 47                 for(RG int i=1;i<=n;i++)
 48                 {
 49                     if(strcmp(a[i],all[i_1_1-1])==0)
 50                     {
 51                         i_1_1--;
 52                         break;
 53                     }
 54                 }
 55             }
 56         }
 57         i_1_1--;
 58         printf("%s ",c);
 59         int len=strlen(c);
 60         for(RG int i=0;i<len;i++)
 61         {
 62             c[i]=c[i]+32;
 63         }
 64         //读入
 65         if(len<i_1_1)//分类讨论
 66         {
 67             puts("is not a valid abbreviation");
 68         }
 69         else if(len==i_1_1)
 70         {
 71             int ans=1;
 72             for(RG int i=0;i<len;i++)
 73             {
 74                 int len2=strlen(all[i+1]);
 75                 RG int tmp=0;
 76                 for(RG int j=0;j<len2;j++)
 77                 {
 78                     if(all[i+1][j]==c[i]) tmp++;
 79                 }
 80                 if(tmp) ans*=tmp;
 81                 else
 82                 {
 83                     puts("is not a valid abbreviation");
 84                     break;
 85                 }
 86             }
 87             printf("can be formed in %d ways
",ans);
 88         }
 89         else //重点
 90         {
 91             memset(sum,0,sizeof(sum));
 92             memset(f,0,sizeof(f));
 93             memset(tag,0,sizeof(tag));
 94             memset(cut,0,sizeof(cut));
 95             memset(ma,0,sizeof(ma));
 96             int cz=strlen(all[1]);
 97             for(int i=2;i<=i_1_1;i++)
 98             {
 99                  strcpy(all[1]+cz,all[i]);
100                  cut[i-1]=cz;
101                  cz=strlen(all[1]);
102             }
103             //连串
104             cut[i_1_1]=cz;
105             for(int i=0;i<cz;i++)
106             {
107                 int j=all[1][i]-'a';
108                 ma[j][++sum[j]]=i;
109             }
110             //记字母位置
111             for(int k=0;k<len;k++)
112             {
113                 int id=c[k]-'a';
114                 int ci=min(k+1,i_1_1);
115                 int ti=1;
116                 for(int i=0;i<cz;i++)
117                 {
118                     tag1[i]=tag[i];
119                 }//备份
120                 for(int i=1;i<=sum[id]&&ma[id][i]<cut[ci];i++)
121                 {
122                     while(ma[id][i]>=cut[ti]) ti++;
123                     if(i_1_1-ti>len-k-1) continue;
124                     if(k==0)
125                     {
126                         tag1[ma[id][i]]=0;//标记
127                         f[k][i]=1;//初始值
128                     }
129                     else
130                     {
131                         int t=c[k-1]-'a';
132                         for(int j=1;j<=sum[t];j++)
133                         {
134                             if(ma[t][j]<ma[id][i]&&ma[t][j]>=cut[ti-2]&&tag[ma[t][j]]==k-1)
135                             {
136                                 f[k][i]+=f[k-1][j];//转移
137                                 tag1[ma[id][i]]=k;//标记×2
138                             }
139                         }
140                     }
141                 }
142                 for(int i=0;i<cz;i++)
143                 {
144                     tag[i]=tag1[i];//备份
145                 }
146             }
147             int ans=0;
148             for(int i=1;i<=sum[c[len-1]-'a'];i++)
149             {
150                 ans+=f[len-1][i];//求和
151             }
152             if(ans)
153             {
154                 printf("can be formed in %d ways
",ans);
155             }
156             else puts("is not a valid abbreviation");
157         }
158     }
159     return 0;
160 }
原文地址:https://www.cnblogs.com/Renyi-Fan/p/7736540.html