簡體   English   中英

這個正則表達式如何找到三角形數字?

[英]How does this regex find triangular numbers?

這是一系列教育正則表達式文章的一部分,這是對嵌套引用概念的溫和介紹。

前幾個三角形數字是:

 1 = 1
 3 = 1 + 2
 6 = 1 + 2 + 3
10 = 1 + 2 + 3 + 4
15 = 1 + 2 + 3 + 4 + 5

有很多方法可以檢查數字是否為三角形。 有一種有趣的技術使用正則表達式如下:

  • 給定n ,我們首先創建一個長度為n的字符串, 其中填充相同的字符
  • 然后我們將此字符串與模式^(\\1.|^.)+$進行匹配
    • 當且僅當此模式與字符串匹配時, n為三角形

以下是一些片段,表明它適用於多種語言:

PHP(在ideone.com上)

$r = '/^(\1.|^.)+$/';

foreach (range(0,50) as $n) {
  if (preg_match($r, str_repeat('o', $n))) {
     print("$n ");
  }
}

Java(在ideone.com上)

for (int n = 0; n <= 50; n++) {
    String s = new String(new char[n]);
    if (s.matches("(\\1.|^.)+")) {
        System.out.print(n + " ");
    }
}

C#(在ideone.com上)

Regex r = new Regex(@"^(\1.|^.)+$");

for (int n = 0; n <= 50; n++) {
    if (r.IsMatch("".PadLeft(n))) {
       Console.Write("{0} ", n);
    }
}

所以這個正則表達式似乎有效,但有人可以解釋一下嗎?

類似的問題

說明

這是模式的示意性細分:

from beginning…
|         …to end
|         |
^(\1.|^.)+$
 \______/|___match
  group 1    one-or-more times

(…) 括號定義捕獲組1,並且該組與+ 重復匹配 此子模式使用^$ 錨定 ,以查看它是否可以匹配整個字符串。

組1嘗試匹配this|that 替換

  • \\1. ,也就是說,第1組匹配(自引用!),加上“任意”字符之一
  • ^. ,也就是說,開頭只是“任何”一個字符

請注意,在第1組中,我們引用了第1組匹配的內容! 這是嵌套/自引用 ,是本示例中介紹的主要思想。 請記住,當重復捕獲組時,通常它只保留最后一次捕獲 ,因此本例中的自引用基本上表示:

“嘗試匹配我上次匹配的內容,再加上一次。這就是我這次匹配的內容。”

與遞歸類似,必須有一個帶有自引用的“基本案例”。 在第一次迭代+ ,1組沒有捕獲任何東西(這是一樣的話說,它開始與一個空字符串)。 因此,引入第二個替換,作為“初始化”組1的一種方式,即當它在字符串的開頭時允許捕獲一個字符。

因此,當用+重復時,組1首先嘗試匹配1個字符,然后是2,然后是3,然后是4,等等。這些數字的總和是三角形數字。


進一步的探索

請注意,為簡化起見,我們使用了與輸入相同的重復字符組成的字符串。 現在我們知道這個模式是如何工作的,我們可以看到這個模式也可以匹配像"1121231234""aababc"等字符串。

另請注意,如果我們發現n是三角形數,即n = 1 + 2 + ... + k ,則組1在末尾捕獲的字符串的長度將為k

這兩點都顯示在以下C#片段中( 也可在ideone.com上看到 ):

Regex r = new Regex(@"^(\1.|^.)+$");

Console.WriteLine(r.IsMatch("aababc"));     // True
Console.WriteLine(r.IsMatch("1121231234")); // True
Console.WriteLine(r.IsMatch("iLoveRegEx")); // False

for (int n = 0; n <= 50; n++) {
    Match m = r.Match("".PadLeft(n));
    if (m.Success) {
       Console.WriteLine("{0} = sum(1..{1})", n, m.Groups[1].Length);
    }
}
// 1 = sum(1..1)
// 3 = sum(1..2)
// 6 = sum(1..3)
// 10 = sum(1..4)
// 15 = sum(1..5)
// 21 = sum(1..6)
// 28 = sum(1..7)
// 36 = sum(1..8)
// 45 = sum(1..9)

味道說明

並非所有口味都支持嵌套引用。 始終熟悉您正在使用的風格的怪癖 (因此,無論何時提出與正則表達式相關的問題,它幾乎總是有助於提供此信息)。

在大多數情況下,標准正則表達式匹配機制會嘗試查看模式是否可以匹配輸入字符串的任何部分 (可能,但不一定是整個輸入)。 這意味着您應該記住在必要時始終使用^$錨定您的模式。

Java略有不同,因為String.matchesPattern.matchesMatcher.matches嘗試將模式與整個輸入字符串進行匹配。 這就是為什么在上面的片段中可以省略錨點的原因。

請注意,在其他上下文中,您可能需要使用\\A\\Z錨點。 例如,在多行模式下^$匹配輸入中每行的開頭和結尾。

最后一件事是,在.NET正則表達式,其實你可以得到所有重復捕獲組提出的中間捕獲。 在大多數風格中,你不能:所有中間捕獲都丟失了,你只能保留最后一個。

相關問題


獎金材料:使用正則表達式找到兩個人的力量!

通過非常輕微的修改,您可以使用此處介紹的相同技術來查找二次方的功能。

這是您想要利用的基本數學屬性:

  • 1 = 1
  • 2 =(1)+ 1
  • 4 =(1 + 2)+ 1
  • 8 =(1 + 2 + 4)+ 1
  • 16 =(1 + 2 + 4 + 8)+ 1
  • 32 =(1 + 2 + 4 + 8 + 16)+ 1

解決方案如下(但請先嘗試自己解決!!!!)

(請參閱PHPJavaC#中的 ideone.com):

^(\\1\\1|^.)*.$

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM