如何计算Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch中的字母数量?

82

我如何计算Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch中的字母数量?

print(len('Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch'))

说58

如果那么容易,我就不会问你了,是吧?!

维基百科说(https://en.wikipedia.org/wiki/Llanfairpwllgwyngyll#Placename_and_toponymy)

该名称的长格式是英国最长的地名之一,在世界上也是最长的之一,共58个字符(51个“字母”,因为“ch”和“ll”是双字母组合,在威尔士语中被视为单个字母)。

所以我想数一下,得到答案51。

好的。

print(len(['Ll','a','n','f','a','i','r','p','w','ll','g','w','y','n','g','y','ll','g','o','g','e','r','y','ch','w','y','r','n','d','r','o','b','w','ll','ll','a','n','t','y','s','i','l','i','o','g','o','g','o','g','o','ch']))
51

嗯,但那是作弊,显然我想使用单词作为输入,而不是列表。

维基百科还说威尔士语中的双字母组有ch、dd、ff、ng、ll、ph、rh、th

https://en.wikipedia.org/wiki/Welsh_orthography#Digraphs

所以我们开始吧。让我们把长度加起来,然后去掉重复计算。

word='Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch'
count=len(word)
print('starting with count of',count)
for index in range(len(word)-1):
  substring=word[index]+word[index+1]
  if substring.lower() in ['ch','dd','ff','ng','ll','ph','rh','th']:
    print('taking off double counting of',substring)
    count=count-1
print(count)

这让我有了一定的进展。

starting with count of 58
taking off double counting of Ll
taking off double counting of ll
taking off double counting of ng
taking off double counting of ll
taking off double counting of ch
taking off double counting of ll
taking off double counting of ll
taking off double counting of ll
taking off double counting of ch
49

看来我减少的太多了。我应该得到51。现在的一个问题是,使用“llll”它找到了3个“ll”,而不是两个,因此需要修复。(不能重叠。)
然后还有另一个问题。“ng”。维基百科没有提到名称中有字母“ng”,但它被列为我引用的页面上的一个二合字。
维基百科在这里给我们一些线索:“可能需要其他信息来区分真正的二合字和字母的并置”。它举了“llongyfarch”的例子,在这个词中,“ng”只是一个“字母的并置”,而在“llong”中则是一个二合字。
所以,“Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch”是那些-ng-不仅仅是“字母的并置”的单词之一。
显然电脑无法知道这一点。所以我将不得不给它“维基百科”所谈论的那些“额外信息”。
所以无论如何,我决定查看在线词典http://geiriadur.ac.uk/gpc/gpc.html,你可以看到,如果查找“llongyfarch”(维基百科的例子,其中有“字母的并置”),则会在n和g之间显示一条竖线,但如果查找“llong”则不会这样做。

screenshot from dictionary (llongyfarch)

screenshot from dictionary (llong)

所以我决定,我们需要通过在输入字符串中放置一个|来提供额外的信息,就像字典中一样,这样算法就知道ng位实际上是两个字母。但是,我显然不希望|本身被计算为一个字母。
现在我有了这些输入:
word='llong'
ANSWER NEEDS TO BE 3 (ll o ng)

word='llon|gyfarch'
ANSWER NEEDS TO BE 9 (ll o n g y f a r ch)

word='Llanfairpwllgwyn|gyllgogerychwyrndrobwllllantysiliogogogoch'
ANSWER NEEDS TO BE 51 (Ll a n f a i r p w ll g w y n g y ll g o g e r y ch w y r n d r o b w ll ll a n t y s i l i o g o g o g o ch)

还有这些双字母组合的列表:

['ch','dd','ff','ng','ll','ph','rh','th']

规则如下:

  1. 忽略大小写

  2. 如果看到双字母,则将其视为1个字母

  3. 从左到右工作,因此 llllll + ll,而不是 l + ll + l

  4. 如果看到 | 不计入,但不能完全忽略它,它在那里是为了防止 ng 成为双字母

我希望它能以正确的方式计算为51,并且出于正确的原因,而不仅仅是偶然得出。

现在我得到了51,但这只是偶然的,因为它将 | 计为一个字母(高了1个),然后用 llll 减去了一个多余的(低了1个) - 错误抵消了

它对 llong 的计算是正确的(3)。

它对 llon|gyfarch 的计算是错误的(10) - 又一次计算了 |

我该如何以正确的方式修复它?


既然您要测量的仅是一个单词,而且您已经知道该单词及其长度,为什么不只需创建一个常量字符串来包含该字符串和一个常量整数来包含字符串的长度,然后就完成了呢?没有必要在代码中这样做,对吧? - raddevus
我对Python不是很了解。在执行count=count-1之后,你能否添加index=index+1来跳过下一个字母? - rhavelka
1
所以我对Python并不是很了解,但我想它们一定有一些关于字符串文化的概念吧?例如,在.NET中,您可以设置应用程序的文化,根据此文化,它会以不同的方式处理某些字符。除非您想要从头开始实现这个功能,否则请忽略此评论。 - Max Young
如果是C#,我可以提供 "ch dd ff ng ll ph rh th |".Split().ToList().ForEach(a => sb.Replace(a, a == "|" ? ".": "")); //sb is a stringbuilder - 只需将每个双字母替换为字符串中不存在的字符,最后将 | 替换为空即可得到字符串长度。虽然我不是Python开发人员,但同样的过程应该也适用于Python,即将双字母替换为单个字符。 - Caius Jard
2
“th”和“sh”是英语中的双字母组,但我从未遇到过任何人认为它们是“单一字母”,在字形意义上。您要求计数“音位”,它们映射到用字母书写的语言时通常会出现困难。您已经确定的音节断点只是其中的一个歧义性。 - Xophmeister
很明显,威尔士语和英语在这方面的操作方式是不同的。例如,如果你使用我发布的字典链接按字母顺序浏览单词(通过使用“下一个单词”和“上一个单词”链接-请注意右上角的链接将界面设置为英语),则“cywystlwr”出现在“ch”开头的单词之前-因为字母“ch”在字母表中出现在字母“c”之后。 - Madarch
4个回答

58

像许多与字符串有关的问题一样,可以通过正则表达式简单地解决此问题。

>>> word = 'Llanfairpwllgwyn|gyllgogerychwyrndrobwllllantysiliogogogoch'
>>> import re
>>> pattern = re.compile(r'ch|dd|ff|ng|ll|ph|rh|th|[^\W\d_]', flags=re.IGNORECASE)
>>> len(pattern.findall(word))
51

字符类[^\W\d_](取自这里)匹配非数字或下划线的单词字符,即包括带附加符号的字母。


条件的顺序重要吗?由于它首先出现,ll 会优先于 a 到 z 吗?更具体地说,这是一个正则表达式特定的事情,还是每种语言都有自己的实现? - Max Young
3
@MaxYoung 是的,部分顺序是为什么双字母语素优先于单个字母的原因; 在我看过的每个正则表达式引擎中,这通常是正确的。在Python中,文档特别指出:“扫描目标字符串时,从左到右尝试由'|'分隔的RE”,因此这是指定的行为,可以放心依赖。 - kaya3
@benjessop--你的模式不加最后的|(ng^yf)会产生相同的结果吗? - DarrylG
7
还有一个问题是威尔士语使用了一些从英语借用的词汇和短语,而且并不总是将其拼写为威尔士语的形式,因此你不能完全指望双字母组合是双字母组合... :-| 啊,自然语言真是太有趣了。 :-) - T.J. Crowder
2
@benjessop,(ng ^ yf)是什么意思?当“^”表示字符串的开头时,它能匹配任何内容吗? - ilkkachu
显示剩余6条评论

21
你可以通过将所有双字母替换为.(或任何其他字符,?也可以)并测量结果字符串的长度(减去|的数量)来获得长度:
def get_length(name):
    name = name.lower()
    doubles = ['ch', 'dd', 'ff', 'ng', 'll', 'ph', 'rh', 'th']
    for double in doubles:
        name = name.replace(double, '.')
    return len(name) - name.count('|')

name = 'Llanfairpwllgwyn|gyllgogerychwyrndrobwllllantysiliogogogoch'
print(get_length(name))
>>> 51

+1,因为非常简单,我从未想过只需对被结合的字符进行标记化处理。我有一种感觉,我将不得不将此应用于我一直在开发的算法中,以便检测日文文本中的重复字符,但其中重复是正确的。在日语中,我遇到的问题是例如“哈哈哈”将是三个相同的字符连续在一起,但理论上可以是我所说的前两个字符和最后一个字符是一个助词。 - Max Young
在这种情况下它运行良好。如果您将此方法应用于其他字符串,则需要确保中间变量不包含原始字符串中不存在的二元组。 - Eric Duminil

10
  1. 逐个字母遍历字符串
  2. 如果当前指针在第n个位置并且 s[n:n+2] 是一个双字母组合,那么将该双字母组合作为键添加或增加到字典中,并将指针也增加1,以便不从第二个双字母组合字符开始计算。如果它不是一个双字母组合,只需将字母添加或增加到字典中并继续到下一个字母。
  3. 如果看到 | 字符,请忽略它,直接跳过。
  4. 别忘了将所有字母转换成小写。

当你遍历完所有字母后,循环结束并将字典中所有计数相加。

这是我的代码,它可在您的三个示例上运行:

from collections import defaultdict

digraphs=['ch','dd','ff','ng','ll','ph','rh','th']
breakchars=['|']


def welshcount(word):
    word = word.lower()
    index = 0
    counts = defaultdict(int)  # keys start at 0 if not already present
    while index < len(word):
        if word[index:index+2] in digraphs:
            counts[word[index:index+2]] += 1
            index += 1
        elif word[index] in breakchars:
            pass  # in case you want to do something here later
        else:  # plain old letter
            counts[word[index]] += 1

        index += 1

    return sum(counts.values())

word1='llong'
#ANSWER NEEDS TO BE 3 (ll o ng)

word2='llon|gyfarch'
#ANSWER NEEDS TO BE 9 (ll o n g y f a r ch)

word3='Llanfairpwllgwyn|gyllgogerychwyrndrobwllllantysiliogogogoch'
#ANSWER NEEDS TO BE 51 (Ll a n f a i r p w ll g w y n g y ll g o g e r y ch w y r n d r o b w ll ll a n t y s i l i o g o g o g o ch)

print(welshcount(word1))
print(welshcount(word2))
print(welshcount(word3))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接