10.6 句子 2409

题目

明明与可可经常在网上聊天,但最近他们发现父母们可能偷看了他们的隐私谈话,因此他们发明了一些加密语言的方法。
所有合法的单词在给定的单词列表中。一个句子有一些合法单词连续组成(没有空格)。每个单词可以出现任意多次。特殊的加密方法是:每个单词在传送之后,它的字母有可能打乱了重新排列。这次加密的代价定义为:加密前的单词和加密后的单词有多少位置上的字母不相同。如:“abc”变为“abc”,则代价为0;如果变为“acb”、“cba”或“bac”则代价为2;如果变为“bca”或“cab”则代价为3。
对于接收到的句子,解码成加密前的句子可能有多种方案,但明明和可可知道只有句子的加密总代价最小的方案才是真正的原句。面对这么复杂的加密语言,他们的父母当然看不懂。但不幸的是,他们自己也搞不懂了,他们需要你帮助研究。
对于给定的合法单词列表和加密后的句子,求出有合法单词通过加密形成现在句子的最小代价。如果没有可能,则输出-1。
提示:如果一个单词出现多次,每次加密后的单词不一定相同

输入样例
4
one
two
three
there
neotowheret
输出样例
8
样例解释
“one”->“neo” 代价为3;
“two”->“tow” 代价为2;
“three”->“heret”代价为3;
“there”->“heret”代价为5;
最小代价=3+2+3=8.
数据范围
合法单词数:1<=n<=50;每个单词长度:1<=len<=50;句子长度:1<=lens<=100
所有单词和句子中的字符都是小写字母“a”~“z”

题解

动态规划,很容易理解的
设f[i]表示从1到i的句子组成合法句子的最小代价
f[i]=min(f[i],f[i-s]+w)
s为当前单词的长度
w为将加密后的当前单词转换为合法单词的最小代价(可以统计单词中每个字母出现的次数,进行比较)

O(26n2lenth)

代码

var
  n,i,j,k,ans,min:longint;
  s:string;
  a:array[1..50,1..26]of longint;
  t:array[1..50]of string;

function night(b:string;var c:longint):boolean;
var
  i,j,e,f,g:longint;
  d:array[1..26]of longint;
begin
  night:=true;
  fillchar(d,sizeof(d),0);
  for i:=1 to length(b) do
    inc(d[ord(b[i])-96]);
  g:=0;
  for i:=1 to n do
    begin
      e:=1;
      if length(t[i])<>length(b) then e:=0 else
      for j:=1 to 26 do
        if a[i,j]<>d[j] then begin e:=0;break;end;
      if (e=1) then
        begin
          f:=0;
          g:=1;
          for j:=1 to length(t[i]) do
            if t[i][j]<>b[j] then inc(f);
          if f<c then c:=f;
        end;
    end;
  if g=0 then exit(false);
end;

procedure dfs(k,m:longint);
var
  i,j,c:longint;
begin
  if (k>length(s))and(m<ans) then begin ans:=m;exit;end;
    for j:=min+k-1 to length(s) do
      if night(copy(s,k,j-k+1),c) then
        begin
          dfs(j+1,m+c);
          if (j+1>=length(s))and(m+c=5) then
            begin
              c:=0;
            end;
          night(copy(s,k,j-k+1),c);
        end;
end;

begin
  readln(n);
  min:=50;
  for i:=1 to n do
    begin
      readln(s);
      t[i]:=s;
      if length(s)<min then min:=length(s);
      for j:=1 to length(s) do
        inc(a[i,ord(s[j])-96]);
    end;
  readln(s);
  ans:=1000;
  dfs(1,0);
  if ans<>1000 then writeln(ans) else writeln(-1);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值