Tutaq ki, — giriş sətiridir. Alt sətir s_i~...~s_j palindromdur, əgər aşağıdakı şərtlərdən biri yerinə yetirilirsə:
i \ge j (alt sətir ya boşdur, ya da bir simvoldan ibarətdir);
s_i = s_j və alt sətir s_{i+1}~...~s_{j-1} palindromdur;
IsPal(i, j) funksiyası s_i ... s_j palindromdursa , əks halda qaytarır. Bu funksiyanın dəyərlərini pal[i][j] hüceyrələrində saxlayırıq.
Tutaq ki, f(i, j) funksiyası sətir s_i~...~s_j-nin palindrom bölməsinə görə lazım olan minimal kəsim sayını qaytarır. Bu funksiyanın dəyərlərini dp[i][j] massivinin hüceyrələrində saxlayacağıq. Onda məsələnin cavabı f(0, s.size() - 1) dəyəri olacaq.
Qeyd edək ki, f(i, i) = 0, çünki bir hərfdən ibarət sətir artıq palindromdur.
f(i, j)-i hesablamaq üçün alt sətir s_i~...~s_j-ni iki hissəyə bölək: s_i~...~s_k və s_{k+1}~...~s_j~(i \le k < j). Rekursiv olaraq s_i~...~s_k və s_{k+1}~...~s_j sətirləri üzərində məsələləri həll edirik. k~(i \le k < j) elə bir dəyərini axtarırıq ki, f(i, k) + f(k + 1, j) + 1 cəmi minimal olsun. əlavə kəsmənin bir kəsim olaraq s_k və s_{k+1} simvolları arasında edildiyini göstərir. Beləliklə, aşağıdakı rekurrent əlaqəni əldə edirik:
Misal
Məsələn, "" adlı simvolluq sətir üçün bizdə var:
Cəm k = 3 üçün ən kiçik dəyərləri alır:
Çünki "" və "" sətirləri palindromdur, bu səbəbdən f(1, 3) = f(4, 7) = 0.
Alqoritmin həyata keçirilməsi
Konstantalar, giriş sətiri və işçi massivləri elan edək.
#define MAX 1001
#define INF 0x3F3F3F3F
int dp[MAX][MAX], pal[MAX][MAX];
string s;IsPal(i, j) funksiyası, alt sətir s_i~...~s_j-nin palindrom olub-olmadığını yoxlayır. Alt sətir s_i~...~s_j palindromdur əgər s_i = s_j və s_{i+1}~...~s_{j-1} palindromdur. IsPal(i, j)-nin dəyərlərini pal[i][j] massivinin hüceyrələrində saxlayırıq.
int IsPal(int i, int j)
{
if (i >= j) return pal[i][j] = 1;
if (pal[i][j] != -1) return pal[i][j];
return pal[i][j] = (s[i] == s[j]) && IsPal(i + 1, j - 1);
}f(i, j) funksiyası, alt sətir s_i~...~s_j-ni palindrom bölməsinə görə lazım olan minimal kəsim sayını qaytarır.
int f(int i, int j)
{Əgər i = j, alt sətir s_i~...~s_j bir simvoldan ibarətdir və onu kəsmək mənasızdır. Əgər i > j, alt sətir s_i~...~s_j boş hesab olunur və kəsimlər də tələb olunmur.
if (i >= j) return 0;Əgər f(i, j) dəyəri əvvəllər hesablanıbsa, bu dəyəri dp[i][j] massivindən geri qaytarırıq.
if (dp[i][j] != INF) return dp[i][j];Əgər alt sətir s_i~...~s_j~(i < j) palindromdursa, onu kəsmək mənasızdır. Bu halda minimal kəsim sayı olur.
if (IsPal(i, j)) return dp[i][j] = 0;Alt sətir s_i~...~s_j-ni iki hissəyə bölək: s_i~...~s_k~(i \le k) və s_{k+1}~...~s_j~(k + 1 \le j). Daha sonra hər iki hissə üçün rekursiv olaraq məsələləri həll edirik: s_i~...~s_k və s_{k+1}~...~s_j. k~(i \le k < j) elə bir dəyərini axtarırıq ki, f(i, k) + f(k + 1, j) + 1 cəmi ən kiçik olsun.
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], f(i, k) + f(k + 1, j) + 1);Cavabı geri qaytarırıq dp[i][j] = f(i, j).
return dp[i][j];
}Proqramın əsas hissəsi. Giriş sətirini oxuyuruq.
cin >> s;Massivləri ilkinləşdiririk.
memset(dp, 0x3F, sizeof(dp));
memset(pal, -1, sizeof(pal));Cavabı hesablayır və çıxışa yazırıq.
res = f(0, s.size() - 1);
cout << res << endl;