使用C#中的递归进行字符串/字符字体映射

时间:2012-07-24 09:02:17

标签: c# fonts mapping

我正在创建一个字体映射程序,我想要映射两种不同语言的字体。

例如,Arial字体(英文)中的A将在Kruti Dev Font(印地语)中使用进行映射。

我在数据库中以这种方式创建了这种转换:

Native_Font     |     Foreign_Language_Font
-------------------------------------------
       A        |              अ
       B        |              बी

如果转换仅限于字符,则可以。读取本机字体的每个字符,并在外语字体中查找其匹配的字符。 (我已经完成了)

但现在我必须为字符串做这件事,这就是真正的问题开始的地方。

如果在数据库中提供了字符串,则将其完全转换。但是如果它的转换不存在,那么找到一个字符的转换就少了。

一个例子是

在数据库中

Native_Font     |     Foreign_Language_Font
-------------------------------------------
    Cha         |              चा
     r          |              र
     t          |              ट

和单词Chart用于翻译。

  1. 首先会尝试转换完整字Chart。如果找到它的映射立即给出。
  2. 但如果找不到Chart的直接转换,请转到Char。如果立即找到其映射,则找到t
  3. 的相应字符

    等等

    Chart
    
    Char t
    
    Cha rt
    Cha r t
    
    Ch art
    Ch ar t
    Ch a r t
    
    C hart
    C har t
    C ha r t
    C h a r t
    

    此外,如果未找到映射,则应将其替换为本机字体字符。怎么做?我确信应该使用递归。但是怎么样?

3 个答案:

答案 0 :(得分:1)

我会用一种贪婪的算法来接近它。像这样:

    // warning, untested
    public String Translate(String s, Dictionary<String, String> mapping)
    {
        String result = "";
        if (RecurTranslate(s, mapping, ref result))
            return result;

        throw new Exception("No translation");
    }

    private bool RecurTranslate(String s, Dictionary<String, String> mapping, ref String result)
    {
        if (s.Length == 0)
            return true;

        for (int prefixLen = s.Length; prefixLen > 0; --prefixLen)
        {
            String prefix = s.Substring(0, prefixLen);
            String trans;
            if (mapping.TryGetValue(prefix, out trans))
            {
                if (RecurTranslate(s.Substring(prefixLen), mapping, ref result))
                {
                    result = trans + result;
                    return true;
                }
            }
            else if (prefixLen == 1)
            {   // this branch allows a character to stand for itself
                if (RecurTranslate(s.Substring(prefixLen), mapping, ref result))
                {
                    result = prefix + result;
                    return true;
                }
            }
        }

        return false;
    }

这从前面开始,寻找最大可能的匹配。根据您的数据,其他方法可能会更好 - 例如,按照长度顺序浏览映射以找到最长匹配并从那里拆分:

    private bool RecurTranslate2(String s, Dictionary<String, String> mapping, ref String result)
    {
        if (s.Length == 0)
            return true;

        foreach (var entry in mapping.Where(e => e.Key.Length <= s.Length).OrderByDescending(e => e.Key.Length))
        {
            if (s.Contains(entry.Key))
            {   // split into a before and after
                int idx = s.IndexOf(entry.Key);
                String before = s.Substring(0, idx);
                String after = s.Substring(idx + entry.Key.Length);
                String bRes = "", aRes = "";
                if (RecurTranslate2(before, mapping, ref bRes) && RecurTranslate2(after, mapping, ref aRes))
                {
                    result = aRes + entry.Value + bRes;
                    return true;
                }
            }
        }

        return false;
    }

最后,您可以使用组合这些方法:使用RecurTranslate2,直到达到某个长度阈值,然后切换到RecurTranslate。

回应评论:请参阅新的其他分支以查找失败

答案 1 :(得分:0)

递归版本 - O(2 ^(string.Length-1)) - 不要在更大的字符串上运行

[Test]
    public void StringSplit()
    {

        var splits = AllPossibleSplits("hello".Select(c => c.ToString()).ToList());

        Assert.AreEqual(16, splits.Count);
        Assert.AreEqual("hello", splits.First().First());
        Assert.AreEqual("hello".Length, splits.Last().Count());
    }

    private List<List<string>> AllPossibleSplits(List<string> source)
    {

        if (source.Count == 0)
        {
            return new List<List<string>>();
        }
        if (source.Count == 1)
        {
            return new List<List<string>> { source };
        }
        var firstTwoJoined = new List<string> { source[0] + source[1] };
        firstTwoJoined.AddRange(source.Skip(2));

        var justFirst = new List<string> { source[0] };
        var withoutFirst = AllPossibleSplits(source.Skip(1).ToList());

        var result = new List<List<string>>();
        result.AddRange(AllPossibleSplits(firstTwoJoined));
        result.AddRange(withoutFirst.Select(list => justFirst.Concat(list).ToList()));

        return result;
    }

答案 2 :(得分:0)

我会以不同方式处理此问题,可能使用phonemes。你想要的是一个转换过程

英文文本 - &gt;英语音素 - &gt;每个音素(或组合)的印地语表示 - &gt;印地语音译。

一个开源字符串到音素合成器是eSpeak。使用this方法,您只能从库中获取音素(丢弃波形缓冲区)。现在扫描英语音素并从地图中选择印地语片段。

注意:您可以选择将英语中给定字符串转换为标准音素地图的任何库,eSpeak只是我之前见过的

这种方法应该会产生更好的结果,在我看来是这个问题的“自然”解决方案。

希望这有帮助!